From 29af7a4a17528b455a2ea8e8ae623fca02fd8d80 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Fri, 18 Jul 2025 14:34:44 +0200 Subject: [PATCH 1/3] simplify code --- .../devices/kostal/kostal_plenticore/bat.py | 29 +++--- .../kostal/kostal_plenticore/counter.py | 58 ++++++++---- .../kostal/kostal_plenticore/device.py | 88 +++++++------------ .../kostal/kostal_plenticore/inverter.py | 35 +++++--- 4 files changed, 107 insertions(+), 103 deletions(-) diff --git a/packages/modules/devices/kostal/kostal_plenticore/bat.py b/packages/modules/devices/kostal/kostal_plenticore/bat.py index 2357adae06..4bb40c79ec 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/bat.py +++ b/packages/modules/devices/kostal/kostal_plenticore/bat.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 import logging -from typing import Any, Callable, TypedDict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor -from modules.common.modbus import ModbusDataType from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreBatSetup @@ -15,6 +16,8 @@ class KwargsDict(TypedDict): device_id: int + modbus_id: int + client: ModbusTcpClient_ class KostalPlenticoreBat(AbstractBat): @@ -24,29 +27,27 @@ def __init__(self, component_config: KostalPlenticoreBatSetup, **kwargs: Any) -> def initialize(self) -> None: self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - def read_state(self, reader: Callable[[int, ModbusDataType], Any]) -> BatState: - power = reader(582, ModbusDataType.INT_16) * -1 - soc = reader(514, ModbusDataType.INT_16) + def update(self) -> None: + power = self.client.read_holding_registers(582, ModbusDataType.INT_16, unit=self.modbus_id) * -1 + soc = self.client.read_holding_registers(514, ModbusDataType.INT_16, unit=self.modbus_id) imported, exported = self.sim_counter.sim_count(power) log.debug("raw bat power "+str(power)) - # Speicherladung muss durch Wandlungsverluste und internen Verbrauch korrigiert werden, sonst - # wird ein falscher Hausverbrauch berechnet. Die Verluste fallen hier unter den Tisch. if power < 0: - power = reader(106, ModbusDataType.FLOAT_32) * -1 + power = self.client.read_holding_registers(106, ModbusDataType.FLOAT_32, unit=self.modbus_id) * -1 - return BatState( + bat_state = BatState( power=power, soc=soc, imported=imported, - exported=exported, + exported=exported ) - - def update(self, state): - self.store.set(state) + self.store.set(bat_state) component_descriptor = ComponentDescriptor(configuration_factory=KostalPlenticoreBatSetup) diff --git a/packages/modules/devices/kostal/kostal_plenticore/counter.py b/packages/modules/devices/kostal/kostal_plenticore/counter.py index ed91eb0120..a15115bc6a 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/counter.py +++ b/packages/modules/devices/kostal/kostal_plenticore/counter.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -from typing import Any, Callable, TypedDict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_counter_value_store from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreCounterSetup @@ -12,6 +13,8 @@ class KwargsDict(TypedDict): device_id: int + modbus_id: int + client: ModbusTcpClient_ class KostalPlenticoreCounter(AbstractCounter): @@ -21,33 +24,50 @@ def __init__(self, component_config: KostalPlenticoreCounterSetup, **kwargs: Any def initialize(self) -> None: self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - def get_values(self, reader: Callable[[int, ModbusDataType], Any]) -> CounterState: - power_factor = reader(150, ModbusDataType.FLOAT_32) - currents = [reader(register, ModbusDataType.FLOAT_32) for register in [222, 232, 242]] - voltages = [reader(register, ModbusDataType.FLOAT_32) for register in [230, 240, 250]] - powers = [reader(register, ModbusDataType.FLOAT_32) for register in [224, 234, 244]] - power = reader(252, ModbusDataType.FLOAT_32) - frequency = reader(220, ModbusDataType.FLOAT_32) + def update(self) -> None: + power = self.client.read_holding_registers(252, ModbusDataType.FLOAT_32, unit=self.modbus_id) + imported, exported = self.sim_counter.sim_count(power) + power_factor = self.client.read_holding_registers(150, ModbusDataType.FLOAT_32, unit=self.modbus_id) + currents = [ + self.client.read_holding_registers( + 222, ModbusDataType.FLOAT_32, unit=self.modbus_id), + self.client.read_holding_registers( + 232, ModbusDataType.FLOAT_32, unit=self.modbus_id), + self.client.read_holding_registers( + 242, ModbusDataType.FLOAT_32, unit=self.modbus_id)] + voltages = [ + self.client.read_holding_registers( + 230, ModbusDataType.FLOAT_32, unit=self.modbus_id), + self.client.read_holding_registers( + 240, ModbusDataType.FLOAT_32, unit=self.modbus_id), + self.client.read_holding_registers( + 250, ModbusDataType.FLOAT_32, unit=self.modbus_id)] + powers = [ + self.client.read_holding_registers( + 224, ModbusDataType.FLOAT_32, unit=self.modbus_id), + self.client.read_holding_registers( + 234, ModbusDataType.FLOAT_32, unit=self.modbus_id), + self.client.read_holding_registers( + 244, ModbusDataType.FLOAT_32, unit=self.modbus_id)] + frequency = self.client.read_holding_registers(220, ModbusDataType.FLOAT_32, unit=self.modbus_id) - return CounterState( + counter_state = CounterState( powers=powers, currents=currents, voltages=voltages, power=power, power_factors=[power_factor]*3, - frequency=frequency + frequency=frequency, + imported=imported, + exported=exported ) - - def update_imported_exported(self, state: CounterState) -> CounterState: - state.imported, state.exported = self.sim_counter.sim_count(state.power) - return state - - def update(self, reader: Callable[[int, ModbusDataType], Any]): - self.store.set(self.update_imported_exported(self.get_values(reader))) + self.store.set(counter_state) component_descriptor = ComponentDescriptor(configuration_factory=KostalPlenticoreCounterSetup) diff --git a/packages/modules/devices/kostal/kostal_plenticore/device.py b/packages/modules/devices/kostal/kostal_plenticore/device.py index 87a729beca..87dc441f4c 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/device.py +++ b/packages/modules/devices/kostal/kostal_plenticore/device.py @@ -1,88 +1,64 @@ -# !/usr/bin/env python3 -from enum import IntEnum -from typing import Any, Callable, Iterable, Union -from pymodbus.constants import Endian -import functools +#!/usr/bin/env python3 import logging +from typing import Iterable, Union -from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ from modules.devices.kostal.kostal_plenticore.bat import KostalPlenticoreBat -from modules.devices.kostal.kostal_plenticore.inverter import KostalPlenticoreInverter -from modules.devices.kostal.kostal_plenticore.config import (KostalPlenticore, KostalPlenticoreBatSetup, - KostalPlenticoreCounterSetup, - KostalPlenticoreInverterSetup) from modules.devices.kostal.kostal_plenticore.counter import KostalPlenticoreCounter - +from modules.devices.kostal.kostal_plenticore.inverter import KostalPlenticoreInverter +from modules.devices.kostal.kostal_plenticore.config import KostalPlenticore, KostalPlenticoreBatSetup +from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreCounterSetup, KostalPlenticoreInverterSetup log = logging.getLogger(__name__) -class LegacyCounterPosition(IntEnum): - HOME_CONSUMPTION = 0 - GRID = 1 - - -def update( - components: Iterable[Union[KostalPlenticoreBat, KostalPlenticoreCounter, KostalPlenticoreInverter]], - reader: Callable[[int, modbus.ModbusDataType], Any], - set_inverter_state: bool = True): - battery = next((component for component in components if isinstance(component, KostalPlenticoreBat)), None) - bat_state = battery.read_state(reader) if battery else None - for component in components: - if isinstance(component, KostalPlenticoreInverter): - # Fürs erste nur die WR-Werte nutzen ohne Verlustberechnung. - # power: R575(inverter generation power (actual)) - # exported: R320 (Total yield) - inverter_state = component.read_state(reader) - pv_state = inverter_state - if set_inverter_state: - component.update(pv_state) - elif isinstance(component, KostalPlenticoreCounter): - component.update(reader) - if bat_state: - battery.update(bat_state) - if set_inverter_state is False: - return pv_state - - def create_device(device_config: KostalPlenticore): client = None - reader = None def create_bat_component(component_config: KostalPlenticoreBatSetup): - return KostalPlenticoreBat(component_config, device_id=device_config.id) + nonlocal client + return KostalPlenticoreBat(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_counter_component(component_config: KostalPlenticoreCounterSetup): - return KostalPlenticoreCounter(component_config, device_id=device_config.id) + nonlocal client + return KostalPlenticoreCounter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_inverter_component(component_config: KostalPlenticoreInverterSetup): - return KostalPlenticoreInverter(component_config) + nonlocal client + return KostalPlenticoreInverter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def update_components( - components: Iterable[Union[KostalPlenticoreBat, KostalPlenticoreCounter, KostalPlenticoreInverter]] - ): - nonlocal client, reader + components: Iterable[Union[KostalPlenticoreBat, KostalPlenticoreCounter, KostalPlenticoreInverter]]): + nonlocal client with client: - update(components, reader) + for component in components: + component.update() def initializer(): - nonlocal client, reader - client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - reader = _create_reader(client, device_config.configuration.modbus_id) + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) return ConfigurableDevice( device_config=device_config, initializer=initializer, component_factory=ComponentFactoryByType( - bat=create_bat_component, counter=create_counter_component, inverter=create_inverter_component), - component_updater=MultiComponentUpdater(update_components), + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) ) -def _create_reader(tcp_client: modbus.ModbusTcpClient_, modbus_id: int) -> Callable[[int, modbus.ModbusDataType], Any]: - return functools.partial(tcp_client.read_holding_registers, unit=modbus_id, wordorder=Endian.Little) - - device_descriptor = DeviceDescriptor(configuration_factory=KostalPlenticore) diff --git a/packages/modules/devices/kostal/kostal_plenticore/inverter.py b/packages/modules/devices/kostal/kostal_plenticore/inverter.py index ba6a7db640..966f16b3df 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/inverter.py +++ b/packages/modules/devices/kostal/kostal_plenticore/inverter.py @@ -1,37 +1,44 @@ #!/usr/bin/env python3 -from typing import Any, Callable +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter from modules.common.store import get_inverter_value_store from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreInverterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class KostalPlenticoreInverter(AbstractInverter): - def __init__(self, component_config: KostalPlenticoreInverterSetup) -> None: + def __init__(self, component_config: KostalPlenticoreInverterSetup, **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.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") - def read_state(self, reader: Callable[[int, ModbusDataType], Any]) -> InverterState: - # PV-Anlage kann nichts verbrauchen, also ggf. Register-/Rundungsfehler korrigieren. - power = reader(575, ModbusDataType.INT_16) * -1 - exported = reader(320, ModbusDataType.FLOAT_32) + def update(self) -> None: + power = self.client.read_holding_registers(575, ModbusDataType.INT_16, unit=self.modbus_id) * -1 + exported = self.client.read_holding_registers(320, ModbusDataType.FLOAT_32, unit=self.modbus_id) - return InverterState( + inverter_state = InverterState( power=power, exported=exported ) - - def dc_in_string_1_2(self, reader: Callable[[int, ModbusDataType], Any]): - return reader(260, ModbusDataType.FLOAT_32) + reader(270, ModbusDataType.FLOAT_32) - - def update(self, state): - self.store.set(state) + self.store.set(inverter_state) component_descriptor = ComponentDescriptor(configuration_factory=KostalPlenticoreInverterSetup) From 92a24884dec21214a2662300ac94d562d5f2ed6b Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Fri, 18 Jul 2025 15:10:25 +0200 Subject: [PATCH 2/3] use list comprehension --- .../kostal/kostal_plenticore/counter.py | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/packages/modules/devices/kostal/kostal_plenticore/counter.py b/packages/modules/devices/kostal/kostal_plenticore/counter.py index a15115bc6a..ea1836e29d 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/counter.py +++ b/packages/modules/devices/kostal/kostal_plenticore/counter.py @@ -34,27 +34,12 @@ def update(self) -> None: power = self.client.read_holding_registers(252, ModbusDataType.FLOAT_32, unit=self.modbus_id) imported, exported = self.sim_counter.sim_count(power) power_factor = self.client.read_holding_registers(150, ModbusDataType.FLOAT_32, unit=self.modbus_id) - currents = [ - self.client.read_holding_registers( - 222, ModbusDataType.FLOAT_32, unit=self.modbus_id), - self.client.read_holding_registers( - 232, ModbusDataType.FLOAT_32, unit=self.modbus_id), - self.client.read_holding_registers( - 242, ModbusDataType.FLOAT_32, unit=self.modbus_id)] - voltages = [ - self.client.read_holding_registers( - 230, ModbusDataType.FLOAT_32, unit=self.modbus_id), - self.client.read_holding_registers( - 240, ModbusDataType.FLOAT_32, unit=self.modbus_id), - self.client.read_holding_registers( - 250, ModbusDataType.FLOAT_32, unit=self.modbus_id)] - powers = [ - self.client.read_holding_registers( - 224, ModbusDataType.FLOAT_32, unit=self.modbus_id), - self.client.read_holding_registers( - 234, ModbusDataType.FLOAT_32, unit=self.modbus_id), - self.client.read_holding_registers( - 244, ModbusDataType.FLOAT_32, unit=self.modbus_id)] + currents = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.modbus_id) for reg in [222, 232, 242]] + voltages = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.modbus_id) for reg in [230, 240, 250]] + powers = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.modbus_id) for reg in [224, 234, 244]] frequency = self.client.read_holding_registers(220, ModbusDataType.FLOAT_32, unit=self.modbus_id) counter_state = CounterState( From 1f14e7a414de6ac6f78e4747d0825b8790d922e3 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Fri, 18 Jul 2025 15:11:45 +0200 Subject: [PATCH 3/3] call simcounter after power calculation --- packages/modules/devices/kostal/kostal_plenticore/bat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/modules/devices/kostal/kostal_plenticore/bat.py b/packages/modules/devices/kostal/kostal_plenticore/bat.py index 4bb40c79ec..2ea9003784 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/bat.py +++ b/packages/modules/devices/kostal/kostal_plenticore/bat.py @@ -36,10 +36,10 @@ def initialize(self) -> None: def update(self) -> None: power = self.client.read_holding_registers(582, ModbusDataType.INT_16, unit=self.modbus_id) * -1 soc = self.client.read_holding_registers(514, ModbusDataType.INT_16, unit=self.modbus_id) - imported, exported = self.sim_counter.sim_count(power) - log.debug("raw bat power "+str(power)) if power < 0: power = self.client.read_holding_registers(106, ModbusDataType.FLOAT_32, unit=self.modbus_id) * -1 + imported, exported = self.sim_counter.sim_count(power) + log.debug("raw bat power "+str(power)) bat_state = BatState( power=power,