From f1cb85885ad922d665f4c94b2ef12e3b27d44d71 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 1 Apr 2026 08:27:51 +0200 Subject: [PATCH 1/5] calculate GridSetPoint --- .../modules/devices/victron/victron/bat.py | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/packages/modules/devices/victron/victron/bat.py b/packages/modules/devices/victron/victron/bat.py index 4782034fe5..d6c6f9c429 100644 --- a/packages/modules/devices/victron/victron/bat.py +++ b/packages/modules/devices/victron/victron/bat.py @@ -12,6 +12,7 @@ from modules.common.store import get_bat_value_store from modules.devices.victron.victron.config import VictronBatSetup from modules.common.utils.peak_filter import PeakFilter +from control import data log = logging.getLogger(__name__) @@ -34,6 +35,7 @@ def initialize(self) -> None: self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.last_mode = 'Undefined' self.peak_filter = PeakFilter("bat", self.component_config.id, self.fault_state) + self.current_power = 0 def update(self) -> None: modbus_id = self.component_config.configuration.modbus_id @@ -49,6 +51,7 @@ def update(self) -> None: exported=exported ) self.store.set(bat_state) + self.current_power = power def set_power_limit(self, power_limit: Optional[int]) -> None: modbus_id = self.component_config.configuration.modbus_id @@ -79,43 +82,35 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: self.__tcp_client.write_register(39, 1, data_type=ModbusDataType.UINT_16, unit=vebus_id) self.last_mode = 'stop' elif power_limit < 0: + evu_power = data.data.counter_all_data.get_evu_counter().data.get.power + set_power = (power_limit - self.current_power) + evu_power + log.debug(f"Aktive Batteriesteuerung Victron:" + f"Speicher soll mit {power_limit} W geladen werden. \n" + f"Aktueller Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W" + f"EVU - Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") if self.last_mode != 'discharge': - # ESS Mode 3 für externe Steuerung und auf L1 wird entladen - self.__tcp_client.write_register(2902, 3, data_type=ModbusDataType.UINT_16, unit=modbus_id) - self.__tcp_client.write_register(39, 0, data_type=ModbusDataType.UINT_16, unit=vebus_id) + self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'discharge' - # Die maximale Entladeleistung begrenzen auf 5000W - if phases == 3: - power_limit = power_limit / 3 - power_value = int(min(power_limit, 5000)) + power_value = int(max(set_power, -5000)) log.debug(f"Aktive Batteriesteuerung. Victron mit {phases} Phase(n). " - f"Batterie wird mit {power_value} W pro Phase entladen.") + f"Batterie wird mit {power_value} W entladen.") self.__tcp_client.write_register( - 37, power_value & 0xFFFF, data_type=ModbusDataType.INT_16, unit=vebus_id) - if phases == 3: - self.__tcp_client.write_register( - 40, power_value & 0xFFFF, data_type=ModbusDataType.INT_16, unit=vebus_id) - self.__tcp_client.write_register( - 41, power_value & 0xFFFF, data_type=ModbusDataType.INT_16, unit=vebus_id) + 2716, power_value, data_type=ModbusDataType.INT_32, unit=modbus_id) elif power_limit > 0: + evu_power = data.data.counter_all_data.get_evu_counter().data.get.power + set_power = (power_limit - self.current_power) + evu_power + log.debug(f"Aktive Batteriesteuerung Victron:" + f"Speicher soll mit {power_limit} W geladen werden. \n" + f"Aktueller Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W" + f"EVU - Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") if self.last_mode != 'charge': - # ESS Mode 3 für externe Steuerung und auf L1 wird entladen - self.__tcp_client.write_register(2902, 3, data_type=ModbusDataType.UINT_16, unit=modbus_id) - self.__tcp_client.write_register(39, 0, data_type=ModbusDataType.UINT_16, unit=vebus_id) + self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'charge' - # Die maximale Entladeleistung begrenzen auf 5000W - if phases == 3: - power_limit = power_limit / 3 - power_value = int(min(power_limit, 5000)) + power_value = int(min(set_power, 5000)) log.debug(f"Aktive Batteriesteuerung. Victron mit {phases} Phase(n). " - f"Batterie wird mit {power_value} W pro Phase geladen.") + f"Batterie wird mit {power_value} W geladen.") self.__tcp_client.write_register( - 37, power_value & 0xFFFF, data_type=ModbusDataType.INT_16, unit=vebus_id) - if phases == 3: - self.__tcp_client.write_register( - 40, power_value & 0xFFFF, data_type=ModbusDataType.INT_16, unit=vebus_id) - self.__tcp_client.write_register( - 41, power_value & 0xFFFF, data_type=ModbusDataType.INT_16, unit=vebus_id) + 2716, power_value, data_type=ModbusDataType.INT_32, unit=modbus_id) def power_limit_controllable(self) -> bool: return True From da0229961d2eae4bd6507961bf92fcbc86a7e4b9 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 1 Apr 2026 11:07:45 +0200 Subject: [PATCH 2/5] use ESS Mode 2 only --- .../modules/devices/victron/victron/bat.py | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/modules/devices/victron/victron/bat.py b/packages/modules/devices/victron/victron/bat.py index d6c6f9c429..973d88fb1b 100644 --- a/packages/modules/devices/victron/victron/bat.py +++ b/packages/modules/devices/victron/victron/bat.py @@ -55,31 +55,27 @@ def update(self) -> None: def set_power_limit(self, power_limit: Optional[int]) -> None: modbus_id = self.component_config.configuration.modbus_id - vebus_id = self.component_config.configuration.vebus_id # Wenn Victron Dynamic ESS aktiv, erfolgt keine weitere Regelung in openWB dynamic_ess_mode = self.__tcp_client.read_holding_registers(5400, ModbusDataType.UINT_16, unit=modbus_id) if dynamic_ess_mode == 1: log.debug("Dynamic ESS Mode ist aktiv, daher erfolgt keine Regelung des Speichers durch openWB") return - phases = self.__tcp_client.read_holding_registers(28, ModbusDataType.UINT_16, unit=vebus_id) - if phases == 1: - log.debug("Einphasiger Speicher erkannt, Speichersteuerung nur auf der ersten Phase.") - else: - log.debug("Mehrphasiger Speicher erkannt, Speichersteuerung auf 3 Phasen.") if power_limit is None: log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter") if self.last_mode is not None: - # ESS Mode 1 für Selbstregelung mit Phasenkompensation setzen - self.__tcp_client.write_register(2902, 1, data_type=ModbusDataType.UINT_16, unit=modbus_id) - self.__tcp_client.write_register(39, 0, data_type=ModbusDataType.UINT_16, unit=vebus_id) + # ESS Mode 2 und Leistung EVU auf 0 setzen für Selbstregelung + self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_register(2702, 100, data_type=ModbusDataType.UINT_16, unit=modbus_id) + # self.__tcp_client.write_register(39, 0, data_type=ModbusDataType.UINT_16, unit=vebus_id) + self.__tcp_client.write_register(2716, 0, data_type=ModbusDataType.INT_32, unit=modbus_id) self.last_mode = None elif power_limit == 0: log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen") if self.last_mode != 'stop': - # ESS Mode 3 für externe Steuerung und keine Entladung - self.__tcp_client.write_register(2902, 3, data_type=ModbusDataType.UINT_16, unit=modbus_id) - self.__tcp_client.write_register(39, 1, data_type=ModbusDataType.UINT_16, unit=vebus_id) + # ESS Mode 2 und Discharge Power 0% für externe Steuerung und keine Entladung + self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_register(2702, 0, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'stop' elif power_limit < 0: evu_power = data.data.counter_all_data.get_evu_counter().data.get.power @@ -90,10 +86,9 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: f"EVU - Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") if self.last_mode != 'discharge': self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_register(2702, 100, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'discharge' power_value = int(max(set_power, -5000)) - log.debug(f"Aktive Batteriesteuerung. Victron mit {phases} Phase(n). " - f"Batterie wird mit {power_value} W entladen.") self.__tcp_client.write_register( 2716, power_value, data_type=ModbusDataType.INT_32, unit=modbus_id) elif power_limit > 0: @@ -105,10 +100,9 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: f"EVU - Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") if self.last_mode != 'charge': self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_register(2702, 100, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'charge' power_value = int(min(set_power, 5000)) - log.debug(f"Aktive Batteriesteuerung. Victron mit {phases} Phase(n). " - f"Batterie wird mit {power_value} W geladen.") self.__tcp_client.write_register( 2716, power_value, data_type=ModbusDataType.INT_32, unit=modbus_id) From 51d5b6822eb1a2d63b77292ac508a5b56229eec4 Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 1 Apr 2026 12:26:51 +0200 Subject: [PATCH 3/5] fixes --- .../modules/devices/victron/victron/bat.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/modules/devices/victron/victron/bat.py b/packages/modules/devices/victron/victron/bat.py index 2ebb66bcb0..3807ea07c6 100644 --- a/packages/modules/devices/victron/victron/bat.py +++ b/packages/modules/devices/victron/victron/bat.py @@ -36,6 +36,7 @@ def initialize(self) -> None: self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.last_mode = 'Undefined' self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) + self.current_power = 0 def update(self) -> None: modbus_id = self.component_config.configuration.modbus_id @@ -81,30 +82,31 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: evu_power = data.data.counter_all_data.get_evu_counter().data.get.power set_power = (power_limit - self.current_power) + evu_power log.debug(f"Aktive Batteriesteuerung Victron:" - f"Speicher soll mit {power_limit} W geladen werden. \n" - f"Aktueller Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W" - f"EVU - Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") + f"Speicher soll mit {power_limit} W entladen werden. \n" + f"Aktuelle Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W " + f"EVU-Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") if self.last_mode != 'discharge': self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.__tcp_client.write_register(2702, 100, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'discharge' - power_value = int(max(set_power, -5000)) + + # Setzen der angestrebten EVU-Leistung, Speicher versucht seine Leistung + # anzupassen um den Zielwert zu erreichen self.__tcp_client.write_register( - 2716, power_value, data_type=ModbusDataType.INT_32, unit=modbus_id) + 2716, set_power, data_type=ModbusDataType.INT_32, unit=modbus_id) elif power_limit > 0: evu_power = data.data.counter_all_data.get_evu_counter().data.get.power set_power = (power_limit - self.current_power) + evu_power log.debug(f"Aktive Batteriesteuerung Victron:" f"Speicher soll mit {power_limit} W geladen werden. \n" - f"Aktueller Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W" - f"EVU - Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") + f"Aktuelle Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W " + f"EVU-Leistung um {power_limit - self.current_power} W anpassen auf {set_power} W") if self.last_mode != 'charge': self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.__tcp_client.write_register(2702, 100, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.last_mode = 'charge' - power_value = int(min(set_power, 5000)) self.__tcp_client.write_register( - 2716, power_value, data_type=ModbusDataType.INT_32, unit=modbus_id) + 2716, set_power, data_type=ModbusDataType.INT_32, unit=modbus_id) def power_limit_controllable(self) -> bool: return True From 09a2ba14a42e1f09f1d1e46c2cee583daabddb2f Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 1 Apr 2026 12:41:17 +0200 Subject: [PATCH 4/5] remove vebus_id --- packages/helpermodules/update_config.py | 19 ++++++++++++++++++- .../modules/devices/victron/victron/config.py | 3 +-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 99244dba79..9393f46c84 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -57,7 +57,7 @@ class UpdateConfig: - DATASTORE_VERSION = 118 + DATASTORE_VERSION = 119 valid_topic = [ "^openWB/bat/config/bat_control_permitted$", @@ -3028,3 +3028,20 @@ def upgrade(topic: str, payload) -> Optional[dict]: return {topic: configuration_payload} self._loop_all_received_topics(upgrade) self._append_datastore_version(118) + + def upgrade_datastore_119(self) -> None: + def upgrade(topic: str, payload) -> None: + if re.search("openWB/system/device/[0-9]+", topic) is not None: + payload = decode_payload(payload) + index = get_index(topic) + if payload.get("type") == "victron": + for component_topic, component_payload in self.all_received_topics.items(): + if re.search(f"openWB/system/device/{index}/component/[0-9]+/config$", + component_topic) is not None: + config_payload = decode_payload(component_payload) + if (config_payload["type"] == "bat" and + config_payload["configuration"].get("vebus_id") is not None): + config_payload["configuration"] = config_payload["configuration"].pop("vebus_id") + return {topic: config_payload} + self._loop_all_received_topics(upgrade) + self._append_datastore_version(119) diff --git a/packages/modules/devices/victron/victron/config.py b/packages/modules/devices/victron/victron/config.py index b6b3ddd1e1..808ac01526 100644 --- a/packages/modules/devices/victron/victron/config.py +++ b/packages/modules/devices/victron/victron/config.py @@ -24,9 +24,8 @@ def __init__(self, class VictronBatConfiguration: - def __init__(self, modbus_id: int = 100, vebus_id: int = 228): + def __init__(self, modbus_id: int = 100): self.modbus_id = modbus_id - self.vebus_id = vebus_id class VictronBatSetup(ComponentSetup[VictronBatConfiguration]): From c23df1c385822396cd7d035297868d96c18f50ab Mon Sep 17 00:00:00 2001 From: ndrsnhs Date: Wed, 1 Apr 2026 13:01:45 +0200 Subject: [PATCH 5/5] GridSetPoint 0 while blocking discharge --- packages/modules/devices/victron/victron/bat.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/modules/devices/victron/victron/bat.py b/packages/modules/devices/victron/victron/bat.py index 3807ea07c6..f49dfc4f40 100644 --- a/packages/modules/devices/victron/victron/bat.py +++ b/packages/modules/devices/victron/victron/bat.py @@ -65,18 +65,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: - # ESS Mode 2 und Leistung EVU auf 0 setzen für Selbstregelung + # ESS Mode 2 und Leistung EVU auf 0kW setzen für Selbstregelung self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.__tcp_client.write_register(2702, 100, data_type=ModbusDataType.UINT_16, unit=modbus_id) - # self.__tcp_client.write_register(39, 0, data_type=ModbusDataType.UINT_16, unit=vebus_id) self.__tcp_client.write_register(2716, 0, data_type=ModbusDataType.INT_32, unit=modbus_id) self.last_mode = None elif power_limit == 0: log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen") if self.last_mode != 'stop': # ESS Mode 2 und Discharge Power 0% für externe Steuerung und keine Entladung + # Leistung an EVU-Punkt auf 0kW setzen -> Eigenregelung bei laden und Entladen verhindern self.__tcp_client.write_register(2902, 2, data_type=ModbusDataType.UINT_16, unit=modbus_id) self.__tcp_client.write_register(2702, 0, data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_register(2716, 0, data_type=ModbusDataType.INT_32, unit=modbus_id) self.last_mode = 'stop' elif power_limit < 0: evu_power = data.data.counter_all_data.get_evu_counter().data.get.power