diff --git a/packages/modules/common/tasmota.py b/packages/modules/common/tasmota.py deleted file mode 100644 index cf150140a9..0000000000 --- a/packages/modules/common/tasmota.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -import logging - -from modules.common.component_state import CounterState -from modules.common import req - -log = logging.getLogger(__name__) - - -class Tasmota: - def __init__(self, - device_id: int, - ip_address: str, - phase: int) -> None: - self.__device_id = device_id - self.__ip_address = ip_address - if phase: - self.__phase = phase - else: - self.__phase = 1 - - def get_CounterState(self) -> CounterState: - url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" - response = req.get_http_session().get(url, timeout=5).json() - - voltages = [0.0, 0.0, 0.0] - powers = [0.0, 0.0, 0.0] - currents = [0.0, 0.0, 0.0] - power_factors = [0.0, 0.0, 0.0] - - if 'ENERGY' in response['StatusSNS']: - voltages[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Voltage']) - powers[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Power']) - power = sum(powers) - currents[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Current']) - power_factors[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Factor']) - imported = float(response['StatusSNS']['ENERGY']['Total']*1000) - exported = 0.0 - - counter_state = CounterState( - imported=imported, - exported=exported, - power=power, - voltages=voltages, - currents=currents, - powers=powers, - power_factors=power_factors - ) - else: - power = float(response['StatusSNS']['Itron']['Power']) - imported = float(response['StatusSNS']['Itron']['E_in']) - exported = float(response['StatusSNS']['Itron']['E_out']) - - counter_state = CounterState( - imported=imported, - exported=exported, - power=power - ) - - log.debug("tasmota.get_CounterState:\nurl=" + url + - "\nresponse=" + str(response) + - "\nCounterState=" + str(counter_state)) - return counter_state - - def set_PowerOn(self) -> str: - url = "http://" + self.__ip_address + "/cm?cmnd=Power%20on" - response = req.get_http_session().get(url, timeout=3).json() - return response['POWER'] - - def setPowerOff(self) -> str: - url = "http://" + self.__ip_address + "/cm?cmnd=Power%20off" - response = req.get_http_session().get(url, timeout=3).json() - return response['POWER'] diff --git a/packages/modules/devices/tasmota/tasmota/bat.py b/packages/modules/devices/tasmota/tasmota/bat.py new file mode 100644 index 0000000000..d38f1a4090 --- /dev/null +++ b/packages/modules/devices/tasmota/tasmota/bat.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict +import logging + +from modules.devices.tasmota.tasmota.config import TasmotaBatSetup +from modules.common.abstract_device import AbstractBat +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_bat_value_store +from modules.common.simcount import SimCounter +from modules.common import req +from modules.common.component_state import BatState + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + phase: int + + +class TasmotaBat(AbstractBat): + def __init__(self, component_config: TasmotaBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.__phase: int = self.kwargs['phase'] + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" + response = req.get_http_session().get(url, timeout=5).json() + + if 'ENERGY' in response['StatusSNS']: + currents = [0.0, 0.0, 0.0] + + power = float(response['StatusSNS']['ENERGY']['Power']) + currents[self.__phase-1] = (response['StatusSNS']['ENERGY']['Current']), 0.0, 0.0 + imported = float(response['StatusSNS']['ENERGY']['Total']*1000) + _, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + currents=currents, + imported=imported, + exported=exported + ) + else: + power = float(response['StatusSNS']['Itron']['Power']) + imported = float(response['StatusSNS']['Itron']['E_in']*1000) + exported = float(response['StatusSNS']['Itron']['E_out']*1000) + + bat_state = BatState( + power=power, + imported=imported, + exported=exported + ) + + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=TasmotaBatSetup) diff --git a/packages/modules/devices/tasmota/tasmota/config.py b/packages/modules/devices/tasmota/tasmota/config.py index bd874e760d..8f6785b83c 100644 --- a/packages/modules/devices/tasmota/tasmota/config.py +++ b/packages/modules/devices/tasmota/tasmota/config.py @@ -35,3 +35,31 @@ def __init__(self, id: int = 0, configuration: TasmotaCounterConfiguration = None) -> None: super().__init__(name, type, id, configuration or TasmotaCounterConfiguration()) + + +class TasmotaInverterConfiguration: + def __init__(self): + pass + + +class TasmotaInverterSetup(ComponentSetup[TasmotaInverterConfiguration]): + def __init__(self, + name: str = "Tasmota Wechselrichterzähler", + type: str = "inverter", + id: int = 0, + configuration: TasmotaInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or TasmotaInverterConfiguration()) + + +class TasmotaBatConfiguration: + def __init__(self): + pass + + +class TasmotaBatSetup(ComponentSetup[TasmotaBatConfiguration]): + def __init__(self, + name: str = "Tasmota Speicherzähler", + type: str = "bat", + id: int = 0, + configuration: TasmotaBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or TasmotaBatConfiguration()) diff --git a/packages/modules/devices/tasmota/tasmota/counter.py b/packages/modules/devices/tasmota/tasmota/counter.py index 01c35f0d90..b0330725ba 100644 --- a/packages/modules/devices/tasmota/tasmota/counter.py +++ b/packages/modules/devices/tasmota/tasmota/counter.py @@ -4,10 +4,12 @@ from modules.devices.tasmota.tasmota.config import TasmotaCounterSetup from modules.common.abstract_device import AbstractCounter -from modules.common.tasmota import Tasmota from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.store import get_counter_value_store +from modules.common.simcount import SimCounter +from modules.common import req +from modules.common.component_state import CounterState log = logging.getLogger(__name__) @@ -26,14 +28,49 @@ def __init__(self, component_config: TasmotaCounterSetup, **kwargs: Any) -> None def initialize(self) -> None: self.__device_id: int = self.kwargs['device_id'] self.__ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.__phase: int = self.kwargs['phase'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__tasmota = Tasmota(self.__device_id, self.__ip_address, self.__phase) def update(self): - log.debug("tasmota.counter.update: " + self.__ip_address) - counter_state = self.__tasmota.get_CounterState() + url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" + response = req.get_http_session().get(url, timeout=5).json() + + if 'ENERGY' in response['StatusSNS']: + voltages = [0.0, 0.0, 0.0] + powers = [0.0, 0.0, 0.0] + currents = [0.0, 0.0, 0.0] + power_factors = [0.0, 0.0, 0.0] + + power = float(response['StatusSNS']['ENERGY']['Power']) + voltages[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Voltage']) + powers[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Power']) + currents[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Current']) + power_factors[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Factor']) + imported = float(response['StatusSNS']['ENERGY']['Total']*1000) + _, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + power=power, + voltages=voltages, + currents=currents, + powers=powers, + power_factors=power_factors, + imported=imported, + exported=exported + ) + else: + power = float(response['StatusSNS']['Itron']['Power']) + imported = float(response['StatusSNS']['Itron']['E_in']*1000) + exported = float(response['StatusSNS']['Itron']['E_out']*1000) + + counter_state = CounterState( + power=power, + imported=imported, + exported=exported + ) + self.store.set(counter_state) diff --git a/packages/modules/devices/tasmota/tasmota/device.py b/packages/modules/devices/tasmota/tasmota/device.py index 58d34f98de..2a187ab957 100644 --- a/packages/modules/devices/tasmota/tasmota/device.py +++ b/packages/modules/devices/tasmota/tasmota/device.py @@ -2,9 +2,11 @@ import logging from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, IndependentComponentUpdater -from modules.devices.tasmota.tasmota.config import Tasmota, TasmotaCounterSetup +from modules.devices.tasmota.tasmota.config import Tasmota, TasmotaCounterSetup, TasmotaInverterSetup, TasmotaBatSetup from modules.common.abstract_device import DeviceDescriptor from modules.devices.tasmota.tasmota.counter import TasmotaCounter +from modules.devices.tasmota.tasmota.inverter import TasmotaInverter +from modules.devices.tasmota.tasmota.bat import TasmotaBat log = logging.getLogger(__name__) @@ -16,10 +18,24 @@ def create_counter_component(component_config: TasmotaCounterSetup): ip_address=device_config.configuration.ip_address, phase=int(device_config.configuration.phase)) + def create_inverter_component(component_config: TasmotaInverterSetup): + return TasmotaInverter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + phase=int(device_config.configuration.phase)) + + def create_bat_component(component_config: TasmotaBatSetup): + return TasmotaBat(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + phase=int(device_config.configuration.phase)) + return ConfigurableDevice( device_config=device_config, component_factory=ComponentFactoryByType( counter=create_counter_component, + inverter=create_inverter_component, + bat=create_bat_component ), component_updater=IndependentComponentUpdater(lambda component: component.update()) ) diff --git a/packages/modules/devices/tasmota/tasmota/inverter.py b/packages/modules/devices/tasmota/tasmota/inverter.py new file mode 100644 index 0000000000..c610831c28 --- /dev/null +++ b/packages/modules/devices/tasmota/tasmota/inverter.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict +import logging + +from modules.devices.tasmota.tasmota.config import TasmotaInverterSetup +from modules.common.abstract_device import AbstractInverter +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_inverter_value_store +from modules.common.simcount import SimCounter +from modules.common import req +from modules.common.component_state import InverterState + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + phase: int + + +class TasmotaInverter(AbstractInverter): + def __init__(self, component_config: TasmotaInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.__phase: int = self.kwargs['phase'] + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" + response = req.get_http_session().get(url, timeout=5).json() + + if 'ENERGY' in response['StatusSNS']: + currents = [0.0, 0.0, 0.0] + + power = float(response['StatusSNS']['ENERGY']['Power']) * -1 + currents[self.__phase-1] = (response['StatusSNS']['ENERGY']['Current']), 0.0, 0.0 + _, exported = self.sim_counter.sim_count(power) + + inverter_state = InverterState( + power=power, + currents=currents, + exported=exported + ) + else: + power = float(response['StatusSNS']['Itron']['Power']) * -1 + exported = float(response['StatusSNS']['Itron']['E_out']*1000) + + inverter_state = InverterState( + power=power, + exported=exported + ) + + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=TasmotaInverterSetup)