From eb3adbec47cb4e75d2cce0689b281d2b777600b0 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 10 Jun 2026 15:48:50 +0200 Subject: [PATCH 01/10] add active control --- .../modules/devices/varta/varta/bat_modbus.py | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index 2ac4cab323..a63f78c0de 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from typing import TypedDict, Any +from typing import TypedDict, Any, Optional +import logging from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -12,6 +13,8 @@ from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType +log = logging.getLogger(__name__) + class KwargsDict(TypedDict): device_id: int @@ -34,20 +37,38 @@ def initialize(self) -> None: self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) def update(self) -> None: - self.set_state(self.get_state()) - - def get_state(self) -> BatState: soc = self.client.read_holding_registers(1068, ModbusDataType.INT_16, unit=self.__modbus_id) power = self.client.read_holding_registers(1066, ModbusDataType.INT_16, unit=self.__modbus_id) self.peak_filter.check_values(power) - return BatState( + imported, exported = self.sim_counter.sim_count(power) + bat_state = BatState( power=power, soc=soc, + imported=imported, + exported=exported ) + self.store.set(bat_state) + + def set_power_limit(self, power_limit: Optional[int]) -> None: + unit=self.__modbus_id + log.debug(f'last_mode: {self.last_mode}') + + if power_limit is None: + log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter") + if self.last_mode is not None: + # hier muss die maximale Entladeleistung des Systems gesetzt werden + # Wir nehmen default -4000W an. Nach 120s setzt sich das Register + # automatisch zurück + self.__tcp_client.write_register(1074, -4000, data_type=ModbusDataType.INT_16, unit=unit) + self.last_mode = None + else: + log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen. " + "Leistungsübergabe und aktive Ladung nicht möglich.") + self.__tcp_client.write_register(1074, 0, data_type=ModbusDataType.INT_16, unit=unit) + self.last_mode = 'stop' - def set_state(self, state: BatState) -> None: - state.imported, state.exported = self.sim_counter.sim_count(state.power) - self.store.set(state) + def power_limit_controllable(self) -> bool: + return True component_descriptor = ComponentDescriptor(configuration_factory=VartaBatModbusSetup) From 24a94ef7b5bd5871a6afa7b78d264f8fa2f78b45 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 10 Jun 2026 15:56:27 +0200 Subject: [PATCH 02/10] fix typo --- packages/modules/devices/varta/varta/bat_modbus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index a63f78c0de..f37dd6b780 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -50,14 +50,14 @@ def update(self) -> None: self.store.set(bat_state) def set_power_limit(self, power_limit: Optional[int]) -> None: - unit=self.__modbus_id + unit = self.__modbus_id log.debug(f'last_mode: {self.last_mode}') if power_limit is None: log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter") if self.last_mode is not None: # hier muss die maximale Entladeleistung des Systems gesetzt werden - # Wir nehmen default -4000W an. Nach 120s setzt sich das Register + # Wir nehmen default -4000W an. Nach 120s setzt sich das Register # automatisch zurück self.__tcp_client.write_register(1074, -4000, data_type=ModbusDataType.INT_16, unit=unit) self.last_mode = None From 10572c81ce1c9259c6a0ea64b559b00765444c39 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 10 Jun 2026 15:58:20 +0200 Subject: [PATCH 03/10] initialize last_mode --- packages/modules/devices/varta/varta/bat_modbus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index f37dd6b780..aeba87b150 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -33,6 +33,7 @@ def initialize(self) -> None: self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) + self.last_mode = 'Undefined' self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) From 6068c504e47e1b26dd74e604de27815efebfdc54 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 10 Jun 2026 15:59:47 +0200 Subject: [PATCH 04/10] fix client --- packages/modules/devices/varta/varta/bat_modbus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index aeba87b150..cb6b8bcd74 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -60,12 +60,12 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: # hier muss die maximale Entladeleistung des Systems gesetzt werden # Wir nehmen default -4000W an. Nach 120s setzt sich das Register # automatisch zurück - self.__tcp_client.write_register(1074, -4000, data_type=ModbusDataType.INT_16, unit=unit) + self.client.write_register(1074, -4000, data_type=ModbusDataType.INT_16, unit=unit) self.last_mode = None else: log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen. " "Leistungsübergabe und aktive Ladung nicht möglich.") - self.__tcp_client.write_register(1074, 0, data_type=ModbusDataType.INT_16, unit=unit) + self.client.write_register(1074, 0, data_type=ModbusDataType.INT_16, unit=unit) self.last_mode = 'stop' def power_limit_controllable(self) -> bool: From b7e61e4f91e1058f9b5969a40e62de39e06e99e5 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 10 Jun 2026 16:03:27 +0200 Subject: [PATCH 05/10] keep legacy update function --- packages/modules/devices/varta/varta/bat_modbus.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index cb6b8bcd74..488772f9c1 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -38,17 +38,20 @@ def initialize(self) -> None: self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) def update(self) -> None: + self.set_state(self.get_state()) + + def get_state(self) -> BatState: soc = self.client.read_holding_registers(1068, ModbusDataType.INT_16, unit=self.__modbus_id) power = self.client.read_holding_registers(1066, ModbusDataType.INT_16, unit=self.__modbus_id) self.peak_filter.check_values(power) - imported, exported = self.sim_counter.sim_count(power) - bat_state = BatState( + return BatState( power=power, soc=soc, - imported=imported, - exported=exported ) - self.store.set(bat_state) + + def set_state(self, state: BatState) -> None: + state.imported, state.exported = self.sim_counter.sim_count(state.power) + self.store.set(state) def set_power_limit(self, power_limit: Optional[int]) -> None: unit = self.__modbus_id From 6ada9c27b21274dffa757c2d4763f3688e52c831 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Thu, 11 Jun 2026 08:04:32 +0200 Subject: [PATCH 06/10] encode int_16 --- packages/modules/common/modbus.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/modules/common/modbus.py b/packages/modules/common/modbus.py index 9f5fc13e50..1958a6e593 100644 --- a/packages/modules/common/modbus.py +++ b/packages/modules/common/modbus.py @@ -193,6 +193,10 @@ def _build_binary_payload(self, byteorder: Endian = Endian.Big, wordorder: Endian = Endian.Big) -> list: builder = BinaryPayloadBuilder(byteorder=byteorder, wordorder=wordorder) + if data_type == ModbusDataType.INT_16: + packed = struct.pack(">h", int(value)) + uint16_value = struct.unpack(">H", packed)[0] + builder.add_16bit_uint(uint16_value) if data_type == ModbusDataType.FLOAT_16: # FLOAT_16 (IEEE 754 Half-Precision) manuelle Konvertierung packed = struct.pack(">e", float(value)) @@ -207,7 +211,8 @@ def _build_binary_payload(self, def write_register(self, address: int, value: Union[int, float], data_type: Optional[ModbusDataType] = None, byteorder: Endian = Endian.Big, wordorder: Endian = Endian.Big, **kwargs): if data_type is not None: - if data_type.bits > 16 or data_type in [ModbusDataType.FLOAT_16, + if data_type.bits > 16 or data_type in [ModbusDataType.INT_16, + ModbusDataType.FLOAT_16, ModbusDataType.FLOAT_32, ModbusDataType.FLOAT_64]: registers = self._build_binary_payload(value, data_type, byteorder, wordorder) From a8cc81f4c5c43728d21b570c8a73ffce507f5049 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Thu, 11 Jun 2026 08:23:33 +0200 Subject: [PATCH 07/10] fix if-elif-else --- packages/modules/common/modbus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/common/modbus.py b/packages/modules/common/modbus.py index 1958a6e593..c07ef27b81 100644 --- a/packages/modules/common/modbus.py +++ b/packages/modules/common/modbus.py @@ -197,7 +197,7 @@ def _build_binary_payload(self, packed = struct.pack(">h", int(value)) uint16_value = struct.unpack(">H", packed)[0] builder.add_16bit_uint(uint16_value) - if data_type == ModbusDataType.FLOAT_16: + elif data_type == ModbusDataType.FLOAT_16: # FLOAT_16 (IEEE 754 Half-Precision) manuelle Konvertierung packed = struct.pack(">e", float(value)) uint16_value = struct.unpack(">H", packed)[0] From 35adb2bde078347bbef3a7928fd8ed37bbbfb0c1 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Thu, 11 Jun 2026 08:26:45 +0200 Subject: [PATCH 08/10] add useful comment --- packages/modules/devices/varta/varta/bat_modbus.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index 488772f9c1..adaeb630b7 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -66,6 +66,8 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: self.client.write_register(1074, -4000, data_type=ModbusDataType.INT_16, unit=unit) self.last_mode = None else: + # Das Register muss kontinuierlich geschrieben werden, da der Speicher + # sonst nach 120s die Steuerung aufhebt. log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen. " "Leistungsübergabe und aktive Ladung nicht möglich.") self.client.write_register(1074, 0, data_type=ModbusDataType.INT_16, unit=unit) From 36d79b7b69bb74a026b9b70389a63608309bfa0f Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Thu, 11 Jun 2026 08:47:40 +0200 Subject: [PATCH 09/10] improve logs; encode int_16 in bat module --- packages/modules/common/modbus.py | 9 ++------- packages/modules/devices/varta/varta/bat_modbus.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/modules/common/modbus.py b/packages/modules/common/modbus.py index c07ef27b81..9f5fc13e50 100644 --- a/packages/modules/common/modbus.py +++ b/packages/modules/common/modbus.py @@ -193,11 +193,7 @@ def _build_binary_payload(self, byteorder: Endian = Endian.Big, wordorder: Endian = Endian.Big) -> list: builder = BinaryPayloadBuilder(byteorder=byteorder, wordorder=wordorder) - if data_type == ModbusDataType.INT_16: - packed = struct.pack(">h", int(value)) - uint16_value = struct.unpack(">H", packed)[0] - builder.add_16bit_uint(uint16_value) - elif data_type == ModbusDataType.FLOAT_16: + if data_type == ModbusDataType.FLOAT_16: # FLOAT_16 (IEEE 754 Half-Precision) manuelle Konvertierung packed = struct.pack(">e", float(value)) uint16_value = struct.unpack(">H", packed)[0] @@ -211,8 +207,7 @@ def _build_binary_payload(self, def write_register(self, address: int, value: Union[int, float], data_type: Optional[ModbusDataType] = None, byteorder: Endian = Endian.Big, wordorder: Endian = Endian.Big, **kwargs): if data_type is not None: - if data_type.bits > 16 or data_type in [ModbusDataType.INT_16, - ModbusDataType.FLOAT_16, + if data_type.bits > 16 or data_type in [ModbusDataType.FLOAT_16, ModbusDataType.FLOAT_32, ModbusDataType.FLOAT_64]: registers = self._build_binary_payload(value, data_type, byteorder, wordorder) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index adaeb630b7..6b97a4520a 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from typing import TypedDict, Any, Optional import logging +import struct from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -60,16 +61,19 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: if power_limit is None: log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter") if self.last_mode is not None: - # hier muss die maximale Entladeleistung des Systems gesetzt werden + # hier muss die maximale Entladeleistung des Systems einmalig gesetzt werden # Wir nehmen default -4000W an. Nach 120s setzt sich das Register # automatisch zurück - self.client.write_register(1074, -4000, data_type=ModbusDataType.INT_16, unit=unit) + packed = struct.pack(">h", int(-4000)) + uint16_value = struct.unpack(">H", packed)[0] + self.client.write_register(1074, uint16_value, data_type=ModbusDataType.UINT_16, unit=unit) self.last_mode = None else: # Das Register muss kontinuierlich geschrieben werden, da der Speicher # sonst nach 120s die Steuerung aufhebt. - log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen. " - "Leistungsübergabe und aktive Ladung nicht möglich.") + log.debug(f"Aktive Batteriesteuerung, übergebene Leistung: {power_limit}W. " + "Leistungsübergabe an Speicher und aktive Ladung nicht möglich. " + "Speicher wird auf Stop gesetzt.") self.client.write_register(1074, 0, data_type=ModbusDataType.INT_16, unit=unit) self.last_mode = 'stop' From 4504a0c1081bb7c0a9253b0d5505689a818210ff Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Thu, 11 Jun 2026 08:57:05 +0200 Subject: [PATCH 10/10] easier int_16 encode --- packages/modules/devices/varta/varta/bat_modbus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index 6b97a4520a..0e6e528ed3 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -64,8 +64,8 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: # hier muss die maximale Entladeleistung des Systems einmalig gesetzt werden # Wir nehmen default -4000W an. Nach 120s setzt sich das Register # automatisch zurück - packed = struct.pack(">h", int(-4000)) - uint16_value = struct.unpack(">H", packed)[0] + max_discharge_w = -4000 + uint16_value = struct.unpack(">H", struct.pack(">h", max_discharge_w))[0] self.client.write_register(1074, uint16_value, data_type=ModbusDataType.UINT_16, unit=unit) self.last_mode = None else: