Skip to content

Commit 3c05987

Browse files
authored
Tasmota inverter and battery (#2519)
* move tasmota to devices; add inverter nd battery * add component currents * update config * add phase selection to all tasmota components * remove unnecessary log messages
1 parent 36fcaae commit 3c05987

6 files changed

Lines changed: 218 additions & 78 deletions

File tree

packages/modules/common/tasmota.py

Lines changed: 0 additions & 73 deletions
This file was deleted.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
from typing import Any, TypedDict
3+
import logging
4+
5+
from modules.devices.tasmota.tasmota.config import TasmotaBatSetup
6+
from modules.common.abstract_device import AbstractBat
7+
from modules.common.component_type import ComponentDescriptor
8+
from modules.common.fault_state import ComponentInfo, FaultState
9+
from modules.common.store import get_bat_value_store
10+
from modules.common.simcount import SimCounter
11+
from modules.common import req
12+
from modules.common.component_state import BatState
13+
14+
log = logging.getLogger(__name__)
15+
16+
17+
class KwargsDict(TypedDict):
18+
device_id: int
19+
ip_address: str
20+
phase: int
21+
22+
23+
class TasmotaBat(AbstractBat):
24+
def __init__(self, component_config: TasmotaBatSetup, **kwargs: Any) -> None:
25+
self.component_config = component_config
26+
self.kwargs: KwargsDict = kwargs
27+
28+
def initialize(self) -> None:
29+
self.__device_id: int = self.kwargs['device_id']
30+
self.__ip_address: str = self.kwargs['ip_address']
31+
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher")
32+
self.__phase: int = self.kwargs['phase']
33+
self.store = get_bat_value_store(self.component_config.id)
34+
self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))
35+
36+
def update(self):
37+
url = "http://" + self.__ip_address + "/cm?cmnd=Status%208"
38+
response = req.get_http_session().get(url, timeout=5).json()
39+
40+
if 'ENERGY' in response['StatusSNS']:
41+
currents = [0.0, 0.0, 0.0]
42+
43+
power = float(response['StatusSNS']['ENERGY']['Power'])
44+
currents[self.__phase-1] = (response['StatusSNS']['ENERGY']['Current']), 0.0, 0.0
45+
imported = float(response['StatusSNS']['ENERGY']['Total']*1000)
46+
_, exported = self.sim_counter.sim_count(power)
47+
48+
bat_state = BatState(
49+
power=power,
50+
currents=currents,
51+
imported=imported,
52+
exported=exported
53+
)
54+
else:
55+
power = float(response['StatusSNS']['Itron']['Power'])
56+
imported = float(response['StatusSNS']['Itron']['E_in']*1000)
57+
exported = float(response['StatusSNS']['Itron']['E_out']*1000)
58+
59+
bat_state = BatState(
60+
power=power,
61+
imported=imported,
62+
exported=exported
63+
)
64+
65+
self.store.set(bat_state)
66+
67+
68+
component_descriptor = ComponentDescriptor(configuration_factory=TasmotaBatSetup)

packages/modules/devices/tasmota/tasmota/config.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,31 @@ def __init__(self,
3535
id: int = 0,
3636
configuration: TasmotaCounterConfiguration = None) -> None:
3737
super().__init__(name, type, id, configuration or TasmotaCounterConfiguration())
38+
39+
40+
class TasmotaInverterConfiguration:
41+
def __init__(self):
42+
pass
43+
44+
45+
class TasmotaInverterSetup(ComponentSetup[TasmotaInverterConfiguration]):
46+
def __init__(self,
47+
name: str = "Tasmota Wechselrichterzähler",
48+
type: str = "inverter",
49+
id: int = 0,
50+
configuration: TasmotaInverterConfiguration = None) -> None:
51+
super().__init__(name, type, id, configuration or TasmotaInverterConfiguration())
52+
53+
54+
class TasmotaBatConfiguration:
55+
def __init__(self):
56+
pass
57+
58+
59+
class TasmotaBatSetup(ComponentSetup[TasmotaBatConfiguration]):
60+
def __init__(self,
61+
name: str = "Tasmota Speicherzähler",
62+
type: str = "bat",
63+
id: int = 0,
64+
configuration: TasmotaBatConfiguration = None) -> None:
65+
super().__init__(name, type, id, configuration or TasmotaBatConfiguration())

packages/modules/devices/tasmota/tasmota/counter.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
from modules.devices.tasmota.tasmota.config import TasmotaCounterSetup
66
from modules.common.abstract_device import AbstractCounter
7-
from modules.common.tasmota import Tasmota
87
from modules.common.component_type import ComponentDescriptor
98
from modules.common.fault_state import ComponentInfo, FaultState
109
from modules.common.store import get_counter_value_store
10+
from modules.common.simcount import SimCounter
11+
from modules.common import req
12+
from modules.common.component_state import CounterState
1113

1214
log = logging.getLogger(__name__)
1315

@@ -26,14 +28,49 @@ def __init__(self, component_config: TasmotaCounterSetup, **kwargs: Any) -> None
2628
def initialize(self) -> None:
2729
self.__device_id: int = self.kwargs['device_id']
2830
self.__ip_address: str = self.kwargs['ip_address']
31+
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug")
2932
self.__phase: int = self.kwargs['phase']
3033
self.store = get_counter_value_store(self.component_config.id)
3134
self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))
32-
self.__tasmota = Tasmota(self.__device_id, self.__ip_address, self.__phase)
3335

3436
def update(self):
35-
log.debug("tasmota.counter.update: " + self.__ip_address)
36-
counter_state = self.__tasmota.get_CounterState()
37+
url = "http://" + self.__ip_address + "/cm?cmnd=Status%208"
38+
response = req.get_http_session().get(url, timeout=5).json()
39+
40+
if 'ENERGY' in response['StatusSNS']:
41+
voltages = [0.0, 0.0, 0.0]
42+
powers = [0.0, 0.0, 0.0]
43+
currents = [0.0, 0.0, 0.0]
44+
power_factors = [0.0, 0.0, 0.0]
45+
46+
power = float(response['StatusSNS']['ENERGY']['Power'])
47+
voltages[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Voltage'])
48+
powers[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Power'])
49+
currents[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Current'])
50+
power_factors[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Factor'])
51+
imported = float(response['StatusSNS']['ENERGY']['Total']*1000)
52+
_, exported = self.sim_counter.sim_count(power)
53+
54+
counter_state = CounterState(
55+
power=power,
56+
voltages=voltages,
57+
currents=currents,
58+
powers=powers,
59+
power_factors=power_factors,
60+
imported=imported,
61+
exported=exported
62+
)
63+
else:
64+
power = float(response['StatusSNS']['Itron']['Power'])
65+
imported = float(response['StatusSNS']['Itron']['E_in']*1000)
66+
exported = float(response['StatusSNS']['Itron']['E_out']*1000)
67+
68+
counter_state = CounterState(
69+
power=power,
70+
imported=imported,
71+
exported=exported
72+
)
73+
3774
self.store.set(counter_state)
3875

3976

packages/modules/devices/tasmota/tasmota/device.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import logging
33

44
from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, IndependentComponentUpdater
5-
from modules.devices.tasmota.tasmota.config import Tasmota, TasmotaCounterSetup
5+
from modules.devices.tasmota.tasmota.config import Tasmota, TasmotaCounterSetup, TasmotaInverterSetup, TasmotaBatSetup
66
from modules.common.abstract_device import DeviceDescriptor
77
from modules.devices.tasmota.tasmota.counter import TasmotaCounter
8+
from modules.devices.tasmota.tasmota.inverter import TasmotaInverter
9+
from modules.devices.tasmota.tasmota.bat import TasmotaBat
810

911
log = logging.getLogger(__name__)
1012

@@ -16,10 +18,24 @@ def create_counter_component(component_config: TasmotaCounterSetup):
1618
ip_address=device_config.configuration.ip_address,
1719
phase=int(device_config.configuration.phase))
1820

21+
def create_inverter_component(component_config: TasmotaInverterSetup):
22+
return TasmotaInverter(component_config,
23+
device_id=device_config.id,
24+
ip_address=device_config.configuration.ip_address,
25+
phase=int(device_config.configuration.phase))
26+
27+
def create_bat_component(component_config: TasmotaBatSetup):
28+
return TasmotaBat(component_config,
29+
device_id=device_config.id,
30+
ip_address=device_config.configuration.ip_address,
31+
phase=int(device_config.configuration.phase))
32+
1933
return ConfigurableDevice(
2034
device_config=device_config,
2135
component_factory=ComponentFactoryByType(
2236
counter=create_counter_component,
37+
inverter=create_inverter_component,
38+
bat=create_bat_component
2339
),
2440
component_updater=IndependentComponentUpdater(lambda component: component.update())
2541
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python3
2+
from typing import Any, TypedDict
3+
import logging
4+
5+
from modules.devices.tasmota.tasmota.config import TasmotaInverterSetup
6+
from modules.common.abstract_device import AbstractInverter
7+
from modules.common.component_type import ComponentDescriptor
8+
from modules.common.fault_state import ComponentInfo, FaultState
9+
from modules.common.store import get_inverter_value_store
10+
from modules.common.simcount import SimCounter
11+
from modules.common import req
12+
from modules.common.component_state import InverterState
13+
14+
log = logging.getLogger(__name__)
15+
16+
17+
class KwargsDict(TypedDict):
18+
device_id: int
19+
ip_address: str
20+
phase: int
21+
22+
23+
class TasmotaInverter(AbstractInverter):
24+
def __init__(self, component_config: TasmotaInverterSetup, **kwargs: Any) -> None:
25+
self.component_config = component_config
26+
self.kwargs: KwargsDict = kwargs
27+
28+
def initialize(self) -> None:
29+
self.__device_id: int = self.kwargs['device_id']
30+
self.__ip_address: str = self.kwargs['ip_address']
31+
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv")
32+
self.__phase: int = self.kwargs['phase']
33+
self.store = get_inverter_value_store(self.component_config.id)
34+
self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))
35+
36+
def update(self):
37+
url = "http://" + self.__ip_address + "/cm?cmnd=Status%208"
38+
response = req.get_http_session().get(url, timeout=5).json()
39+
40+
if 'ENERGY' in response['StatusSNS']:
41+
currents = [0.0, 0.0, 0.0]
42+
43+
power = float(response['StatusSNS']['ENERGY']['Power']) * -1
44+
currents[self.__phase-1] = (response['StatusSNS']['ENERGY']['Current']), 0.0, 0.0
45+
_, exported = self.sim_counter.sim_count(power)
46+
47+
inverter_state = InverterState(
48+
power=power,
49+
currents=currents,
50+
exported=exported
51+
)
52+
else:
53+
power = float(response['StatusSNS']['Itron']['Power']) * -1
54+
exported = float(response['StatusSNS']['Itron']['E_out']*1000)
55+
56+
inverter_state = InverterState(
57+
power=power,
58+
exported=exported
59+
)
60+
61+
self.store.set(inverter_state)
62+
63+
64+
component_descriptor = ComponentDescriptor(configuration_factory=TasmotaInverterSetup)

0 commit comments

Comments
 (0)