From c29c9e8b5f9ed4df9c474970c610960158371cb5 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 09:27:25 +0200 Subject: [PATCH 01/16] Create vendor.py --- packages/modules/devices/marstek/vendor.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 packages/modules/devices/marstek/vendor.py diff --git a/packages/modules/devices/marstek/vendor.py b/packages/modules/devices/marstek/vendor.py new file mode 100644 index 0000000000..20f362f9e9 --- /dev/null +++ b/packages/modules/devices/marstek/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Marstek" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) From 1d0a7ef17146f7eb32744a61eac37a59e11cc08c Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 09:27:50 +0200 Subject: [PATCH 02/16] Create __init__.py --- packages/modules/devices/marstek/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/modules/devices/marstek/__init__.py diff --git a/packages/modules/devices/marstek/__init__.py b/packages/modules/devices/marstek/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/modules/devices/marstek/__init__.py @@ -0,0 +1 @@ + From d5737e2752c9cbdc277f52a3fbb69e3db25f53d4 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 09:28:21 +0200 Subject: [PATCH 03/16] Create __init__.py --- packages/modules/devices/marstek/marstek/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/modules/devices/marstek/marstek/__init__.py diff --git a/packages/modules/devices/marstek/marstek/__init__.py b/packages/modules/devices/marstek/marstek/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/modules/devices/marstek/marstek/__init__.py @@ -0,0 +1 @@ + From f5c31bd5e8565e51e7a2a7f021156be784dacb51 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 09:32:32 +0200 Subject: [PATCH 04/16] Create config.py --- .../modules/devices/marstek/marstek/config.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 packages/modules/devices/marstek/marstek/config.py diff --git a/packages/modules/devices/marstek/marstek/config.py b/packages/modules/devices/marstek/marstek/config.py new file mode 100644 index 0000000000..54b1ef7596 --- /dev/null +++ b/packages/modules/devices/marstek/marstek/config.py @@ -0,0 +1,38 @@ +from typing import Optional + +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +class MarstekConfiguration: + def __init__(self, modbus_id: int = 64, ip_address: Optional[str] = None, port: int = 3600): + self.modbus_id = modbus_id + self.ip_address = ip_address + self.port = port + + +class Saxpower: + def __init__(self, + name: str = "marstek", + type: str = "marstek", + id: int = 0, + configuration: MarstekConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or MarstekConfiguration() + + +class MarstekBatConfiguration: + def __init__(self): + pass + + +class MarstekBatSetup(ComponentSetup[MarstekBatConfiguration]): + def __init__(self, + name: str = "Marstek Speicher", + type: str = "bat", + id: int = 0, + configuration: MarstekBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or MarstekBatConfiguration()) From f3f60980e1276aac2e2fb236aa6421c36295d420 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 09:34:16 +0200 Subject: [PATCH 05/16] Create device.py --- .../modules/devices/marstek/marstek/device.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 packages/modules/devices/marstek/marstek/device.py diff --git a/packages/modules/devices/marstek/marstek/device.py b/packages/modules/devices/marstek/marstek/device.py new file mode 100644 index 0000000000..54c67f0953 --- /dev/null +++ b/packages/modules/devices/marstek/marstek/device.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable + +from modules.common import modbus +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater +from modules.devices.marstek.marstek.bat import MarstekBat +from modules.devices.marstek.marstek.config import Marstek, MarstekBatSetup + +log = logging.getLogger(__name__) + + +def create_device(device_config: Marstek): + client = None + + def create_bat_component(component_config: MarstekBatSetup): + nonlocal client + return MarstekBat(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) + + def update_components(components: Iterable[MarstekBat]): + nonlocal client + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = modbus.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, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Marstek) From da957dacca759a0b499e3ac9b23a22b4916aec87 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:13:23 +0200 Subject: [PATCH 06/16] Create bat.py --- .../modules/devices/marstek/marstek/bat.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 packages/modules/devices/marstek/marstek/bat.py diff --git a/packages/modules/devices/marstek/marstek/bat.py b/packages/modules/devices/marstek/marstek/bat.py new file mode 100644 index 0000000000..fa684a0bfd --- /dev/null +++ b/packages/modules/devices/marstek/marstek/bat.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.common import modbus +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.marstek.marstek.config import MarstekBatSetup # Adjust path if needed + + +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + +class MarstekBat(AbstractBat): + def __init__(self, component_config: MarstekBatSetup, **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.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + modbus_id = self.__modbus_id + + power = self.__tcp_client.read_holding_registers(32202, + ModbusDataType.INT_32, unit=modbus_id) * -1 + soc = self.__tcp_client.read_holding_registers(32104, + ModbusDataType.UINT_16, unit=modbus_id) + imported = self.__tcp_client.read_holding_registers(33000, + ModbusDataType.UINT_32, unit=modbus_id) * 0.01 + exported = self.__tcp_client.read_holding_registers(33002, + ModbusDataType.UINT_32, unit=modbus_id) * 0.01 + + imported, exported = self.sim_counter.sim_count(power) + bat_state = BatState( + power=power, + soc=soc, + imported=imported_scaled, + exported=exported_scaled + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=MarstekBatSetup) From 13c0599d68b10b9b2ed04896e7356b090a05d6d9 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:18:19 +0200 Subject: [PATCH 07/16] SoC factor --- packages/modules/devices/marstek/marstek/bat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/devices/marstek/marstek/bat.py b/packages/modules/devices/marstek/marstek/bat.py index fa684a0bfd..2295a5b05d 100644 --- a/packages/modules/devices/marstek/marstek/bat.py +++ b/packages/modules/devices/marstek/marstek/bat.py @@ -37,7 +37,7 @@ def update(self) -> None: power = self.__tcp_client.read_holding_registers(32202, ModbusDataType.INT_32, unit=modbus_id) * -1 soc = self.__tcp_client.read_holding_registers(32104, - ModbusDataType.UINT_16, unit=modbus_id) + ModbusDataType.UINT_16, unit=modbus_id) * 0.1 imported = self.__tcp_client.read_holding_registers(33000, ModbusDataType.UINT_32, unit=modbus_id) * 0.01 exported = self.__tcp_client.read_holding_registers(33002, From f86254962cfeda80d61b81bab11508a53a1ba5b3 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:28:25 +0200 Subject: [PATCH 08/16] Use simcounter for imp and exp --- packages/modules/devices/marstek/marstek/bat.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/modules/devices/marstek/marstek/bat.py b/packages/modules/devices/marstek/marstek/bat.py index 2295a5b05d..5913356463 100644 --- a/packages/modules/devices/marstek/marstek/bat.py +++ b/packages/modules/devices/marstek/marstek/bat.py @@ -32,23 +32,19 @@ def initialize(self) -> None: self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self) -> None: - modbus_id = self.__modbus_id + unit = self.__modbus_id power = self.__tcp_client.read_holding_registers(32202, - ModbusDataType.INT_32, unit=modbus_id) * -1 + ModbusDataType.INT_32, unit=unit) * -1 soc = self.__tcp_client.read_holding_registers(32104, - ModbusDataType.UINT_16, unit=modbus_id) * 0.1 - imported = self.__tcp_client.read_holding_registers(33000, - ModbusDataType.UINT_32, unit=modbus_id) * 0.01 - exported = self.__tcp_client.read_holding_registers(33002, - ModbusDataType.UINT_32, unit=modbus_id) * 0.01 + ModbusDataType.UINT_16, unit=unit) * 0.1 imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( power=power, soc=soc, - imported=imported_scaled, - exported=exported_scaled + imported=imported, + exported=exported ) self.store.set(bat_state) From ea353007f98ae634542ceda0c11c337fbd098d67 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:35:01 +0200 Subject: [PATCH 09/16] use default modbus_id --- packages/modules/devices/marstek/marstek/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/devices/marstek/marstek/config.py b/packages/modules/devices/marstek/marstek/config.py index 54b1ef7596..d28f6830a8 100644 --- a/packages/modules/devices/marstek/marstek/config.py +++ b/packages/modules/devices/marstek/marstek/config.py @@ -5,7 +5,7 @@ class MarstekConfiguration: - def __init__(self, modbus_id: int = 64, ip_address: Optional[str] = None, port: int = 3600): + def __init__(self, modbus_id: int = 1, ip_address: Optional[str] = None, port: int = 3600): self.modbus_id = modbus_id self.ip_address = ip_address self.port = port From 7d5bb31916bb8a0c0d60ed5123d50c0248c853af Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:40:02 +0200 Subject: [PATCH 10/16] flake8 From fa8e17765ce0ab7922d62d73bce94603e25a8192 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:41:24 +0200 Subject: [PATCH 11/16] flake8 --- packages/modules/devices/marstek/marstek/device.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/modules/devices/marstek/marstek/device.py b/packages/modules/devices/marstek/marstek/device.py index 54c67f0953..be0bae749a 100644 --- a/packages/modules/devices/marstek/marstek/device.py +++ b/packages/modules/devices/marstek/marstek/device.py @@ -17,10 +17,12 @@ def create_device(device_config: Marstek): def create_bat_component(component_config: MarstekBatSetup): nonlocal client - return MarstekBat(component_config, - device_id=device_config.id, - client=client, - modbus_id=device_config.configuration.modbus_id) + return MarstekBat( + component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id, + ) def update_components(components: Iterable[MarstekBat]): nonlocal client From 68ad8aa02a2d9b32c6c29626dc3c3b029b9c4373 Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:43:46 +0200 Subject: [PATCH 12/16] flake8 From 1c4191d207c26d881b2b214455189237611c995b Mon Sep 17 00:00:00 2001 From: SeaSpotter Date: Wed, 20 Aug 2025 10:44:04 +0200 Subject: [PATCH 13/16] flake8 From a39028004377a590da4d39e3bebd6cad1ada8be0 Mon Sep 17 00:00:00 2001 From: LKuemmel <76958050+LKuemmel@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:10:51 +0200 Subject: [PATCH 14/16] Remove blank line From e72db5891c9914b3ada534e2d44ca1e323172eaa Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 20 Aug 2025 14:12:56 +0200 Subject: [PATCH 15/16] remove blank lines --- packages/modules/devices/marstek/__init__.py | 1 - packages/modules/devices/marstek/marstek/__init__.py | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/modules/devices/marstek/__init__.py b/packages/modules/devices/marstek/__init__.py index 8b13789179..e69de29bb2 100644 --- a/packages/modules/devices/marstek/__init__.py +++ b/packages/modules/devices/marstek/__init__.py @@ -1 +0,0 @@ - diff --git a/packages/modules/devices/marstek/marstek/__init__.py b/packages/modules/devices/marstek/marstek/__init__.py index 8b13789179..e69de29bb2 100644 --- a/packages/modules/devices/marstek/marstek/__init__.py +++ b/packages/modules/devices/marstek/marstek/__init__.py @@ -1 +0,0 @@ - From bd82cecd911ea1007ca564d9e5e8ad55e8fd9193 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 20 Aug 2025 14:13:36 +0200 Subject: [PATCH 16/16] typo --- packages/modules/devices/marstek/marstek/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/devices/marstek/marstek/config.py b/packages/modules/devices/marstek/marstek/config.py index d28f6830a8..03218fe2a7 100644 --- a/packages/modules/devices/marstek/marstek/config.py +++ b/packages/modules/devices/marstek/marstek/config.py @@ -11,7 +11,7 @@ def __init__(self, modbus_id: int = 1, ip_address: Optional[str] = None, port: i self.port = port -class Saxpower: +class Marstek: def __init__(self, name: str = "marstek", type: str = "marstek",