Repository navigation
Victron improved bat control #3263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f1cb858
da02299
59b3747
51d5b68
09a2ba1
c23df1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3033
to
+3045
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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} | |
| def upgrade(topic: str, payload) -> Optional[dict]: | |
| # Remove deprecated `vebus_id` from Victron battery component configurations. | |
| # Operate directly on component config topics to avoid iterating inside the callback. | |
| if re.search(r"^openWB/system/device/[0-9]+/component/[0-9]+/config$", topic) is None: | |
| return None | |
| config_payload = decode_payload(payload) | |
| if config_payload.get("type") != "bat": | |
| return None | |
| configuration = config_payload.get("configuration", {}) | |
| if "vebus_id" not in configuration: | |
| return None | |
| # Drop the obsolete field while preserving the rest of the configuration. | |
| configuration.pop("vebus_id", None) | |
| return {topic: config_payload} |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |||||||||||||||||||||||||||||||||
| from modules.devices.victron.victron.config import VictronBatSetup | ||||||||||||||||||||||||||||||||||
| from modules.common.utils.peak_filter import PeakFilter | ||||||||||||||||||||||||||||||||||
| from modules.common.component_type import ComponentType | ||||||||||||||||||||||||||||||||||
| from control import data | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| log = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -35,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 | ||||||||||||||||||||||||||||||||||
|
|
@@ -50,73 +52,62 @@ 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 | ||||||||||||||||||||||||||||||||||
| 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 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(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 | ||||||||||||||||||||||||||||||||||
| # 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(2702, 0, data_type=ModbusDataType.UINT_16, unit=modbus_id) | |
| self.__tcp_client.write_register(2702, 0, data_type=ModbusDataType.UINT_16, unit=modbus_id) | |
| # Beim Wechsel in den Stop-Modus auch den EVU-Setpoint so setzen, | |
| # dass 0 W Speicherleistung angestrebt werden, analog zu den anderen Zweigen. | |
| evu_power = data.data.counter_all_data.get_evu_counter().data.get.power | |
| set_power = (0 - self.current_power) + evu_power | |
| log.debug( | |
| f"Aktive Batteriesteuerung Victron:" | |
| f" Speicher wird gestoppt. \n" | |
| f"Aktuelle Speicherleistung: {self.current_power} W, EVU-Leistung: {evu_power} W " | |
| f"EVU-Leistung um {0 - self.current_power} W anpassen auf {set_power} W" | |
| ) | |
| self.__tcp_client.write_register( | |
| 2716, set_power, data_type=ModbusDataType.INT_32, unit=modbus_id | |
| ) |
Copilot
AI
Apr 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.current_power is used in set_power_limit() but is only assigned in update(). If set_power_limit() is called before the first successful update(), this will raise an AttributeError and break battery control. Initialize self.current_power in initialize() (e.g., to 0) or fall back to the last stored battery power when it is not set yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In
upgrade_datastore_119, the removal ofvebus_idis implemented incorrectly:config_payload["configuration"] = config_payload["configuration"].pop("vebus_id")replaces the wholeconfigurationdict with the popped scalar value. Additionally, the returned topic key usestopic(device topic) instead ofcomponent_topic, so the modified component config would be published to the wrong MQTT topic. Useconfig_payload["configuration"].pop("vebus_id", None)and return{component_topic: config_payload}(keepingconfigurationas a dict).