From 3b3a3fdd24fd32f6fbe172be401bf4c4bd16d6c3 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:35:30 +0200 Subject: [PATCH 01/13] 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 c137b598bdc818b9c6fbfdbe4978ebebea80fd7d Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:36:44 +0200 Subject: [PATCH 02/13] 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 f17c6e05e1db59e93d21e36566a8fa50a0fda256 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:41:28 +0200 Subject: [PATCH 03/13] Create bat.py --- .../modules/devices/marstek/venus_c_e/bat.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/modules/devices/marstek/venus_c_e/bat.py diff --git a/packages/modules/devices/marstek/venus_c_e/bat.py b/packages/modules/devices/marstek/venus_c_e/bat.py new file mode 100644 index 0000000000..b10431e611 --- /dev/null +++ b/packages/modules/devices/marstek/venus_c_e/bat.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +from typing import Optional, TypedDict, Any, Union +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, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.marstek.venus_c_e.config import VenusCEBatSetup + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class VenusCEBat(AbstractBat): + def __init__(self, component_config: VenusCEBatSetup, **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.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.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def _read_reg(self, addr: int, type_: ModbusDataType) -> Union[int, float]: + return self.client.read_holding_registers(addr, type_, unit=self.component_config.configuration.modbus_id) + + def _write_reg(self, addr: int, val: int) -> None: + # Marstek Venus does not work with write_registers! + self.client._delegate.write_register(addr, val, unit=self.component_config.configuration.modbus_id) + + def update(self) -> None: + power = -self._read_reg(32202, ModbusDataType.INT_32) + soc = self._read_reg(32104, ModbusDataType.UINT_16) + + # Marstek Venus has internal counter but it's buggy, hence we cannot use it + 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: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive + # Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird + # None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen. + if (power_limit is None): + self._write_reg(42000, 0x55bb) + else: + self._write_reg(42000, 0x55aa) + if power_limit < 0: + self._write_reg(42010, 2) + self._write_reg(42021, int(min(-power_limit, 2500))) + elif power_limit > 0: + self._write_reg(42010, 1) + self._write_reg(42020, int(min(power_limit, 2500))) + else: + self._write_reg(42010, 0) + + def power_limit_controllable(self) -> bool: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss True zurückgegeben werden. + return True From d18669344e7f019512e7091406874c89c85b51f3 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:42:27 +0200 Subject: [PATCH 04/13] Create config.py --- .../devices/marstek/venus_c_e/config.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 packages/modules/devices/marstek/venus_c_e/config.py diff --git a/packages/modules/devices/marstek/venus_c_e/config.py b/packages/modules/devices/marstek/venus_c_e/config.py new file mode 100644 index 0000000000..76f1a877ee --- /dev/null +++ b/packages/modules/devices/marstek/venus_c_e/config.py @@ -0,0 +1,42 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class VenusCEConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 502): + self.ip_address = ip_address + self.port = port + + +@auto_str +class VenusCE: + def __init__(self, + name: str = "Marstek Venus C, E", + type: str = "venus_c_e", + id: int = 0, + configuration: VenusCEConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or VenusCEConfiguration() + + +@auto_str +class VenusCEBatConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class VenusCEBatSetup(ComponentSetup[VenusCEBatConfiguration]): + def __init__(self, + name: str = "Marstek Venus C, E Speicher", + type: str = "bat", + id: int = 0, + configuration: VenusCEBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or VenusCEBatConfiguration()) From 18117aa5e2a68af33b70ee97eb42669420e20dee Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:43:32 +0200 Subject: [PATCH 05/13] Create device.py --- .../devices/marstek/venus_c_e/device.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 packages/modules/devices/marstek/venus_c_e/device.py diff --git a/packages/modules/devices/marstek/venus_c_e/device.py b/packages/modules/devices/marstek/venus_c_e/device.py new file mode 100644 index 0000000000..b9debb8241 --- /dev/null +++ b/packages/modules/devices/marstek/venus_c_e/device.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.marstek.venus_c_e.bat import VenusCEBat +from modules.devices.marstek.venus_c_e.config import VenusCE, VenusCEBatSetup + +log = logging.getLogger(__name__) + + +def create_device(device_config: VenusCE): + client = None + + def create_bat_component(component_config: VenusCEBatSetup): + nonlocal client + return VenusCEBat(component_config, device_id=device_config.id, client=client) + + def update_components(components: Iterable[VenusCEBat]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + 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, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=VenusCE) From 1584ea05f44aa29d717bd89047652f8cd4ed5474 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:43:57 +0200 Subject: [PATCH 06/13] Create __init__.py --- packages/modules/devices/marstek/venus_c_e/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/modules/devices/marstek/venus_c_e/__init__.py diff --git a/packages/modules/devices/marstek/venus_c_e/__init__.py b/packages/modules/devices/marstek/venus_c_e/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/modules/devices/marstek/venus_c_e/__init__.py @@ -0,0 +1 @@ + From e1e16dd6fa1039bcb5ad69e5af08ccf6745bc719 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:51:19 +0200 Subject: [PATCH 07/13] Update device.py --- packages/modules/devices/marstek/venus_c_e/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/devices/marstek/venus_c_e/device.py b/packages/modules/devices/marstek/venus_c_e/device.py index b9debb8241..6b402bad0e 100644 --- a/packages/modules/devices/marstek/venus_c_e/device.py +++ b/packages/modules/devices/marstek/venus_c_e/device.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Iterable, Union +from typing import Iterable from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext From 60b0450cf13b3f29ef76c10054f1d266a93b906b Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:53:48 +0200 Subject: [PATCH 08/13] Update bat.py --- packages/modules/devices/marstek/venus_c_e/bat.py | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/modules/devices/marstek/venus_c_e/bat.py b/packages/modules/devices/marstek/venus_c_e/bat.py index b10431e611..3b137849a0 100644 --- a/packages/modules/devices/marstek/venus_c_e/bat.py +++ b/packages/modules/devices/marstek/venus_c_e/bat.py @@ -2,7 +2,6 @@ from typing import Optional, TypedDict, Any, Union 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, ModbusTcpClient_ from modules.common.simcount import SimCounter From 336144bc3c3b92b50abf247c8906da8631031dae Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:57:43 +0200 Subject: [PATCH 09/13] Update __init__.py From 7c19234c5e16afa768080294efe0effb21c6a6ae Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:00:25 +0200 Subject: [PATCH 10/13] Add files via upload --- packages/modules/devices/marstek/__init__.py | 1 - 1 file changed, 1 deletion(-) 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 @@ - From dbb62afb49728fb58234124df25b2dab96dda5b9 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:04:13 +0200 Subject: [PATCH 11/13] Add files via upload --- packages/modules/devices/marstek/venus_c_e/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/modules/devices/marstek/venus_c_e/__init__.py b/packages/modules/devices/marstek/venus_c_e/__init__.py index 8b13789179..e69de29bb2 100644 --- a/packages/modules/devices/marstek/venus_c_e/__init__.py +++ b/packages/modules/devices/marstek/venus_c_e/__init__.py @@ -1 +0,0 @@ - From 3a615b5ebfc05c981c1b51e82d5f41005536fff7 Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:19:37 +0200 Subject: [PATCH 12/13] Update bat.py --- packages/modules/devices/marstek/venus_c_e/bat.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/modules/devices/marstek/venus_c_e/bat.py b/packages/modules/devices/marstek/venus_c_e/bat.py index 3b137849a0..1613fb175d 100644 --- a/packages/modules/devices/marstek/venus_c_e/bat.py +++ b/packages/modules/devices/marstek/venus_c_e/bat.py @@ -2,6 +2,7 @@ from typing import Optional, TypedDict, Any, Union 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, ModbusTcpClient_ from modules.common.simcount import SimCounter @@ -68,3 +69,5 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: def power_limit_controllable(self) -> bool: # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss True zurückgegeben werden. return True + +component_descriptor = ComponentDescriptor(configuration_factory=VenusCEBatSetup) From 52b8137e757b9c24aee194ec308d696911bcaabe Mon Sep 17 00:00:00 2001 From: andlem74 <73437243+andlem74@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:24:00 +0200 Subject: [PATCH 13/13] Update bat.py --- packages/modules/devices/marstek/venus_c_e/bat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/modules/devices/marstek/venus_c_e/bat.py b/packages/modules/devices/marstek/venus_c_e/bat.py index 1613fb175d..1f57a668d4 100644 --- a/packages/modules/devices/marstek/venus_c_e/bat.py +++ b/packages/modules/devices/marstek/venus_c_e/bat.py @@ -70,4 +70,5 @@ def power_limit_controllable(self) -> bool: # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss True zurückgegeben werden. return True + component_descriptor = ComponentDescriptor(configuration_factory=VenusCEBatSetup)