diff --git a/modules/bezug_solarworld/main.sh b/modules/bezug_solarworld/main.sh index 0cb9c1ed1..ef1e92f80 100755 --- a/modules/bezug_solarworld/main.sh +++ b/modules/bezug_solarworld/main.sh @@ -12,9 +12,8 @@ fi openwbDebugLog ${DMOD} 2 "Bezug Solarworld IP: ${solarworld_emanagerip}" -bash "$OPENWBBASEDIR/packages/legacy_run.sh" "bezug_solarworld.solarworld" "${solarworld_emanagerip}" >>"$MYLOGFILE" 2>&1 +bash "$OPENWBBASEDIR/packages/legacy_run.sh" "modules.devices.solar_world.device" "counter" "${solarworld_emanagerip}" >>"$MYLOGFILE" 2>&1 ret=$? openwbDebugLog ${DMOD} 2 "RET: ${ret}" - cat "${RAMDISKDIR}/wattbezug" diff --git a/modules/bezug_solarworld/solarworld.py b/modules/bezug_solarworld/solarworld.py deleted file mode 100755 index 0112fccaa..000000000 --- a/modules/bezug_solarworld/solarworld.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -from typing import List -import logging -import requests -import traceback - -from helpermodules.cli import run_using_positional_cli_args - -log = logging.getLogger("Solarworld EVU") - - -def update(solarworld_emanagerip: str): - log.debug('Solarworld IP: ' + solarworld_emanagerip) - - # Auslesen eines Solarworld eManagers über die integrierte JSON-API - emanagerantwort = requests.get( - 'http://'+solarworld_emanagerip+'/rest/solarworld/lpvm/powerAndBatteryData', timeout=3).json() - try: - em_in_watt = emanagerantwort["PowerIn"] - except: - traceback.print_exc() - exit(1) - try: - em_out_watt = emanagerantwort["PowerOut"] - except: - traceback.print_exc() - exit(1) - - # Bezug ist entweder -Out oder In; bei Einspeisung ist 'em_in_watt' immer 0 - bezug_watt = int(em_in_watt - em_out_watt) - - log.debug('Leistung: ' + str(bezug_watt)) - with open("/var/www/html/openWB/ramdisk/wattbezug", "w") as f: - f.write(str(bezug_watt)) - - -def main(argv: List[str]): - run_using_positional_cli_args(update, argv) diff --git a/modules/wr_solarworld/main.sh b/modules/wr_solarworld/main.sh index 4a2dff586..096fbb4e7 100755 --- a/modules/wr_solarworld/main.sh +++ b/modules/wr_solarworld/main.sh @@ -1,6 +1,6 @@ #!/bin/bash -OPENWBBASEDIR=$(cd `dirname $0`/../../ && pwd) +OPENWBBASEDIR=$(cd "$(dirname "$0")/../../" && pwd) RAMDISKDIR="${OPENWBBASEDIR}/ramdisk" #MODULEDIR=$(cd `dirname $0` && pwd) #DMOD="PV" @@ -18,11 +18,8 @@ fi openwbDebugLog ${DMOD} 2 "PV IP: ${solarworld_emanagerip}" -bash "$OPENWBBASEDIR/packages/legacy_run.sh" "wr_solarworld.solarworld" "${solarworld_emanagerip}" >>$MYLOGFILE 2>&1 +bash "$OPENWBBASEDIR/packages/legacy_run.sh" "modules.devices.solar_world.device" "inverter" "${solarworld_emanagerip}" "1" >>"$MYLOGFILE" 2>&1 ret=$? openwbDebugLog ${DMOD} 2 "RET: ${ret}" - - -pvwatt=$( None: + self.name = name + self.type = type + self.id = id + self.configuration = configuration or SolarWorldConfiguration() + + +@auto_str +class SolarWorldCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class SolarWorldCounterSetup(ComponentSetup[SolarWorldCounterConfiguration]): + def __init__(self, + name: str = "SolarWorld Zähler", + type: str = "counter", + id: int = 0, + configuration: SolarWorldCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SolarWorldCounterConfiguration()) + + +@auto_str +class SolarWorldInverterConfiguration: + def __init__(self): + pass + + +@auto_str +class SolarWorldInverterSetup(ComponentSetup[SolarWorldInverterConfiguration]): + def __init__(self, + name: str = "SolarWorld Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: SolarWorldInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SolarWorldInverterConfiguration()) diff --git a/packages/modules/devices/solar_world/counter.py b/packages/modules/devices/solar_world/counter.py new file mode 100644 index 000000000..316565211 --- /dev/null +++ b/packages/modules/devices/solar_world/counter.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +from typing import Dict, Union + +from dataclass_utils import dataclass_from_dict +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.solar_world.config import SolarWorldCounterSetup + + +class SolarWorldCounter: + def __init__(self, device_id: int, component_config: Union[Dict, SolarWorldCounterSetup]) -> None: + self.__device_id = device_id + self.component_config = dataclass_from_dict(SolarWorldCounterSetup, component_config) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.component_info = ComponentInfo.from_component_config(self.component_config) + + def update(self, response): + power = response["PowerIn"] - response["PowerOut"] + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + imported=imported, + exported=exported, + power=power + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SolarWorldCounterSetup) diff --git a/packages/modules/devices/solar_world/device.py b/packages/modules/devices/solar_world/device.py new file mode 100644 index 000000000..227b1ade8 --- /dev/null +++ b/packages/modules/devices/solar_world/device.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Optional, Union, List + +from helpermodules.cli import run_using_positional_cli_args +from modules.common import req +from modules.common.abstract_device import DeviceDescriptor +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.devices.solar_world import counter, inverter +from modules.devices.solar_world.config import ( + SolarWorld, SolarWorldConfiguration, SolarWorldCounterSetup, SolarWorldInverterSetup) +from modules.devices.solar_world.counter import SolarWorldCounter +from modules.devices.solar_world.inverter import SolarWorldInverter + +log = logging.getLogger(__name__) + + +def create_device(device_config: SolarWorld): + def create_counter_component(component_config: SolarWorldCounterSetup): + return SolarWorldCounter(device_config.id, component_config) + + def create_inverter_component(component_config: SolarWorldInverterSetup): + return SolarWorldInverter(device_config.id, component_config) + + def update_components(components: Iterable[Union[SolarWorldCounter, SolarWorldInverter]]): + response = req.get_http_session().get("http://"+str(device_config.configuration.ip_address) + + "/rest/solarworld/lpvm/powerAndBatteryData", timeout=5).json() + for component in components: + component.update(response) + + return ConfigurableDevice( + device_config=device_config, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +COMPONENT_TYPE_TO_MODULE = { + "counter": counter, + "inverter": inverter +} + + +def read_legacy(component_type: str, ip_address: str, num: Optional[int]) -> None: + device_config = SolarWorld(configuration=SolarWorldConfiguration(ip_address=ip_address)) + dev = create_device(device_config) + if component_type in COMPONENT_TYPE_TO_MODULE: + component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory() + else: + raise Exception( + "illegal component type " + component_type + ". Allowed values: " + + ','.join(COMPONENT_TYPE_TO_MODULE.keys()) + ) + component_config.id = num + dev.add_component(component_config) + + log.debug('SolarWorld IP-Adresse: ' + ip_address) + + dev.update() + + +def main(argv: List[str]): + run_using_positional_cli_args(read_legacy, argv) + + +device_descriptor = DeviceDescriptor(configuration_factory=SolarWorld) diff --git a/packages/modules/devices/solar_world/inverter.py b/packages/modules/devices/solar_world/inverter.py new file mode 100644 index 000000000..0acc61ad9 --- /dev/null +++ b/packages/modules/devices/solar_world/inverter.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +from typing import Dict, Union + +from dataclass_utils import dataclass_from_dict +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store +from modules.devices.solar_world.config import SolarWorldInverterSetup + + +class SolarWorldInverter: + def __init__(self, device_id: int, component_config: Union[Dict, SolarWorldInverterSetup]) -> None: + self.__device_id = device_id + self.component_config = dataclass_from_dict(SolarWorldInverterSetup, component_config) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.component_info = ComponentInfo.from_component_config(self.component_config) + + def update(self, response) -> None: + try: + power = response["PowerTotalPV"] * -1 + except ValueError: + # wenn eManager aus bzw. keine Antwort ersetze leeren Wert durch eine 0 + power = 0 + exported = self.sim_counter.sim_count(power)[1] + + inverter_state = InverterState( + power=power, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SolarWorldInverterSetup)