From 4db0759f17b8a8ed6d3cb96922233d05832edcd0 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Tue, 6 Jan 2026 10:45:03 +0100 Subject: [PATCH 1/3] io action: error of io device use remaining energy, show message --- packages/control/io_device.py | 20 ++--- packages/control/limiting_value.py | 2 + packages/control/loadmanagement.py | 23 ++--- packages/control/pv_all.py | 14 ++- packages/modules/io_actions/common.py | 5 ++ .../dimming/api_eebus.py | 20 ++++- .../controllable_consumers/dimming/api_io.py | 20 +++-- .../dimming_direct_control/api.py | 23 +++-- .../ripple_control_receiver/api.py | 88 ++++++++++++------- .../stepwise_control/api_eebus.py | 26 +++++- .../stepwise_control/api_io.py | 30 +++++-- 11 files changed, 182 insertions(+), 89 deletions(-) create mode 100644 packages/modules/io_actions/common.py diff --git a/packages/control/io_device.py b/packages/control/io_device.py index c752fd3e87..c351002901 100644 --- a/packages/control/io_device.py +++ b/packages/control/io_device.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field -from typing import Dict, Optional, Union +from typing import Dict, Optional, Tuple, Union from control import data -from control.limiting_value import LimitingValue +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.constants import NO_ERROR from modules.common.utils.component_parser import get_io_name_by_id from modules.io_actions.controllable_consumers.dimming.api_eebus import DimmingEebus @@ -67,7 +67,7 @@ def _check_fault_state_io_device(self, io_device: int) -> None: if data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2: raise ValueError(LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(io_device))) - def dimming_get_import_power_left(self, device: Dict) -> Optional[float]: + def dimming_get_import_power_left(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]: for action in self.actions.values(): if isinstance(action, (DimmingIo, DimmingEebus)): for d in action.config.configuration.devices: @@ -75,7 +75,7 @@ def dimming_get_import_power_left(self, device: Dict) -> Optional[float]: self._check_fault_state_io_device(action.config.configuration.io_device) return action.dimming_get_import_power_left() else: - return None + return None, LoadmanagementLimit(None, None) def dimming_set_import_power_left(self, device: Dict, used_power: float) -> Optional[float]: for action in self.actions.values(): @@ -84,7 +84,7 @@ def dimming_set_import_power_left(self, device: Dict, used_power: float) -> Opti if d == device: return action.dimming_set_import_power_left(used_power) - def dimming_via_direct_control(self, device: Dict) -> Optional[float]: + def dimming_via_direct_control(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]: for action in self.actions.values(): if isinstance(action, DimmingDirectControl): for d in action.config.configuration.devices: @@ -92,9 +92,9 @@ def dimming_via_direct_control(self, device: Dict) -> Optional[float]: self._check_fault_state_io_device(action.config.configuration.io_device) return action.dimming_via_direct_control() else: - return None + return None, LoadmanagementLimit(None, None) - def ripple_control_receiver(self, device: Dict) -> float: + def ripple_control_receiver(self, device: Dict) -> Tuple[float, LoadmanagementLimit]: for action in self.actions.values(): if isinstance(action, RippleControlReceiver): for d in action.config.configuration.devices: @@ -102,13 +102,13 @@ def ripple_control_receiver(self, device: Dict) -> float: self._check_fault_state_io_device(action.config.configuration.io_device) return action.ripple_control_receiver() else: - return 1 + return 1, LoadmanagementLimit(None, None) - def stepwise_control(self, device_id: int) -> Optional[float]: + def stepwise_control(self, device_id: int) -> Tuple[Optional[float], LoadmanagementLimit]: for action in self.actions.values(): if isinstance(action, (StepwiseControlEebus, StepwiseControlIo)): if device_id in [component["id"] for component in action.config.configuration.devices]: self._check_fault_state_io_device(action.config.configuration.io_device) return action.control_stepwise() else: - return None + return None, LoadmanagementLimit(None, None) diff --git a/packages/control/limiting_value.py b/packages/control/limiting_value.py index 8c19b321d3..558e76e44a 100644 --- a/packages/control/limiting_value.py +++ b/packages/control/limiting_value.py @@ -11,8 +11,10 @@ class LimitingValue(Enum): DIMMING_VIA_DIRECT_CONTROL = ", da die Dimmung per Direkt-Steuerung die Ladeleistung auf 4,2 kW begrenzt." RIPPLE_CONTROL_RECEIVER = (", da der Ladepunkt durch den RSE-Kontakt auf {}% der konfigurierten Anschlussleistung " "reduziert wird.") + CONTROL_STEPWISE = "Leistung begrenzt auf {}%" CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nicht " "gesteuert werden können. Bitte prüfe die Status-Seite.") + MISSING_CONFIFGURATION = ", da die Konfiguration für die Aktion unvollständig ist." @dataclass diff --git a/packages/control/loadmanagement.py b/packages/control/loadmanagement.py index a1567b32ff..911d149e9f 100644 --- a/packages/control/loadmanagement.py +++ b/packages/control/loadmanagement.py @@ -137,35 +137,31 @@ def _limit_by_current(self, def _limit_by_dimming_via_direct_control(self, missing_currents: List[float], cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: - if data.data.io_actions.dimming_via_direct_control({"type": "cp", "id": cp.num}): + value, limit = data.data.io_actions.dimming_via_direct_control({"type": "cp", "id": cp.num}) + if value is not None: phases = 3-missing_currents.count(0) current_per_phase = 4200 / 230 / phases available_currents = [current_per_phase - cp.data.set.target_current if c > 0 else 0 for c in missing_currents] log.debug(f"Dimmung per Direkt-Steuerung: {available_currents}A") - limit = LoadmanagementLimit(LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value, - LimitingValue.DIMMING_VIA_DIRECT_CONTROL) - return available_currents, limit - else: - return missing_currents, LoadmanagementLimit(None, None) + return available_currents, limit def _limit_by_dimming(self, available_currents: List[float], cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: - dimming_power_left = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num}) + dimming_power_left, limit = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num}) if dimming_power_left: if sum(available_currents)*230 > dimming_power_left: phases = 3-available_currents.count(0) overload_per_phase = (sum(available_currents) - dimming_power_left/230)/phases available_currents = [c - overload_per_phase if c > 0 else 0 for c in available_currents] log.debug(f"Reduzierung der Ströme durch die Dimmung: {available_currents}A") - return available_currents, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING) - return available_currents, LoadmanagementLimit(None, None) + return available_currents, limit def _limit_by_ripple_control_receiver(self, available_currents: List[float], cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: - value = data.data.io_actions.ripple_control_receiver({"type": "cp", "id": cp.num}) + value, limit = data.data.io_actions.ripple_control_receiver({"type": "cp", "id": cp.num}) if value != 1: phases = 3-available_currents.count(0) if phases > 1: @@ -177,9 +173,4 @@ def _limit_by_ripple_control_receiver(self, available_currents = [min(max_current*value - cp.data.set.target_current, c) if c > 0 else 0 for c in available_currents] log.debug(f"Reduzierung durch RSE-Kontakt auf {value*100}%, maximal {max_current*value}A") - limit = LoadmanagementLimit( - LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(value*100), - LimitingValue.RIPPLE_CONTROL_RECEIVER) - return available_currents, limit - else: - return available_currents, LoadmanagementLimit(None, None) + return available_currents, limit diff --git a/packages/control/pv_all.py b/packages/control/pv_all.py index e938636a4c..02666c43c1 100644 --- a/packages/control/pv_all.py +++ b/packages/control/pv_all.py @@ -69,15 +69,11 @@ def calc_power_for_all_components(self) -> None: else: if fault_state < module.data.get.fault_state: fault_state = module.data.get.fault_state - limit_value = data.data.io_actions.stepwise_control(module.num) - if limit_value is not None and module.data.get.fault_state == 0: - msg = ( - f"Leistung begrenzt auf {int(limit_value * 100)}%" - if limit_value < 1 - else "Keine Leistungsbegrenzung aktiv." - ) - module.data.get.fault_str = msg - Pub().pub(f"openWB/set/pv/{module.num}/get/fault_str", msg) + limit = data.data.io_actions.stepwise_control(module.num)[1] + if module.data.get.fault_state == 0: + # Fehlermeldung nicht überschreiben + module.data.get.fault_str = limit.message + Pub().pub(f"openWB/set/pv/{module.num}/get/fault_str", limit.message) except Exception: log.exception(f"Fehler im allgemeinen PV-Modul für pv{module.num}") if fault_state == 0: diff --git a/packages/modules/io_actions/common.py b/packages/modules/io_actions/common.py new file mode 100644 index 0000000000..126b773d2f --- /dev/null +++ b/packages/modules/io_actions/common.py @@ -0,0 +1,5 @@ +from control import data + + +def check_fault_state_io_device(io_device: int) -> bool: + return data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2 diff --git a/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py b/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py index 1c1dbadc10..8a1bd51579 100644 --- a/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py +++ b/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py @@ -1,11 +1,15 @@ import logging +from typing import Optional, Tuple from control import data +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules import timecheck from helpermodules.logger import ModifyLoglevelContext from helpermodules.pub import Pub from helpermodules.timecheck import create_timestamp from dataclass_utils import asdict from modules.common.abstract_io import AbstractIoAction +from modules.common.utils.component_parser import get_io_name_by_id +from modules.io_actions.common import check_fault_state_io_device from modules.io_actions.controllable_consumers.dimming.config import DimmingSetup from modules.io_devices.eebus.config import AnalogInputMapping, DigitalInputMapping @@ -42,9 +46,12 @@ def setup(self) -> None: log.debug(f"Dimmen: {self.import_power_left}W inkl. Überschuss") with ModifyLoglevelContext(control_command_log, logging.DEBUG): - if self.dimming_active(): + if self.dimming_active() or check_fault_state_io_device(self.config.configuration.io_device): if self.timestamp is None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) + if check_fault_state_io_device(self.config.configuration.io_device): + control_command_log.info( + "Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.") control_command_log.info(f"Dimmen aktiviert. Übermittelter LPC-Wert: {lpc_value/1000}kWh. " "Leistungswerte vor Ausführung des Steuerbefehls:") @@ -64,11 +71,16 @@ def setup(self) -> None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None) control_command_log.info("Dimmen deaktiviert.") - def dimming_get_import_power_left(self) -> None: + def dimming_get_import_power_left(self) -> Tuple[Optional[float], LoadmanagementLimit]: + if check_fault_state_io_device(self.config.configuration.io_device): + return (self.import_power_left, LoadmanagementLimit( + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( + self.config.configuration.io_device)), + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR)) if self.dimming_active(): - return self.import_power_left + return self.import_power_left, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING) else: - return None + return None, LoadmanagementLimit(None, None) def dimming_set_import_power_left(self, used_power: float) -> None: self.import_power_left -= used_power diff --git a/packages/modules/io_actions/controllable_consumers/dimming/api_io.py b/packages/modules/io_actions/controllable_consumers/dimming/api_io.py index e94b7029f1..81a77b5859 100644 --- a/packages/modules/io_actions/controllable_consumers/dimming/api_io.py +++ b/packages/modules/io_actions/controllable_consumers/dimming/api_io.py @@ -1,11 +1,15 @@ import logging +from typing import Optional, Tuple from control import data +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.logger import ModifyLoglevelContext from helpermodules.pub import Pub from helpermodules.timecheck import create_timestamp from dataclass_utils import asdict from modules.common.abstract_io import AbstractIoAction +from modules.common.utils.component_parser import get_io_name_by_id +from modules.io_actions.common import check_fault_state_io_device from modules.io_actions.controllable_consumers.dimming.config import DimmingSetup log = logging.getLogger(__name__) @@ -49,10 +53,11 @@ def setup(self) -> None: log.debug(f"Dimmen: {self.import_power_left}W inkl. Überschuss") with ModifyLoglevelContext(control_command_log, logging.DEBUG): - if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ - self.dimming_input] == self.dimming_value: + if self.dimming_active() or check_fault_state_io_device(self.config.configuration.io_device): if self.timestamp is None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) + if check_fault_state_io_device(self.config.configuration.io_device): + control_command_log.info("Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.") control_command_log.info("Dimmen aktiviert. Leistungswerte vor Ausführung des Steuerbefehls:") msg = (f"EVU-Zähler: " @@ -71,12 +76,17 @@ def setup(self) -> None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None) control_command_log.info("Dimmen deaktiviert.") - def dimming_get_import_power_left(self) -> None: + def dimming_get_import_power_left(self) -> Tuple[Optional[float], LoadmanagementLimit]: + if check_fault_state_io_device(self.config.configuration.io_device): + return (self.import_power_left, LoadmanagementLimit( + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( + self.config.configuration.io_device)), + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR)) if self.dimming_active(): - return self.import_power_left + return self.import_power_left, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING) elif data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ self.no_dimming_input] == self.no_dimming_value: - return None + return None, LoadmanagementLimit(None, None) else: raise Exception("Pattern passt nicht zur Dimmung.") diff --git a/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py b/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py index eca5d7c60e..6672e9d2c8 100644 --- a/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py +++ b/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py @@ -1,10 +1,14 @@ import logging +from typing import Optional, Tuple from control import data +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.logger import ModifyLoglevelContext from helpermodules.pub import Pub from helpermodules.timecheck import create_timestamp from modules.common.abstract_device import DeviceDescriptor from modules.common.abstract_io import AbstractIoAction +from modules.common.utils.component_parser import get_io_name_by_id +from modules.io_actions.common import check_fault_state_io_device from modules.io_actions.controllable_consumers.dimming_direct_control.config import DimmingDirectControlSetup control_command_log = logging.getLogger("steuve_control_command") @@ -29,13 +33,16 @@ def __init__(self, config: DimmingDirectControlSetup): def setup(self) -> None: with ModifyLoglevelContext(control_command_log, logging.DEBUG): if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ - self.dimming_input] == self.dimming_value: + self.dimming_input] == self.dimming_value or check_fault_state_io_device(self.config.configuration.io_device): device = self.config.configuration.devices[0] if device["type"] == "cp": cp = f"cp{device['id']}" if self.timestamp is None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) if device["type"] == "cp": + if check_fault_state_io_device(self.config.configuration.io_device): + control_command_log.info( + "Fehler des IO-Geräts: Direktsteuerung an Ladepunkt aktiviert für Failsafe-Modus.") control_command_log.info( f"Direktsteuerung an Ladepunkt " f"{data.data.cp_data[cp].data.config.name} aktiviert. " @@ -55,13 +62,19 @@ def setup(self) -> None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None) control_command_log.info("Direktsteuerung deaktiviert.") - def dimming_via_direct_control(self) -> None: - if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ + def dimming_via_direct_control(self) -> Tuple[Optional[float], LoadmanagementLimit]: + if check_fault_state_io_device(self.config.configuration.io_device): + return (4200, LoadmanagementLimit( + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( + self.config.configuration.io_device)), + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR)) + elif data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ self.dimming_input] == self.dimming_value: - return 4200 + return (4200, LoadmanagementLimit(LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value, + LimitingValue.DIMMING_VIA_DIRECT_CONTROL)) elif data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ self.no_dimming_input] == self.no_dimming_value: - return None + return None, LoadmanagementLimit(None, None) else: raise Exception("Pattern passt nicht zur Dimmung per Direktsteuerung.") diff --git a/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py b/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py index eb27361b6d..157e805882 100644 --- a/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py +++ b/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py @@ -1,10 +1,14 @@ import logging +from typing import Tuple from control import data +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.logger import ModifyLoglevelContext from helpermodules.pub import Pub from helpermodules.timecheck import create_timestamp from modules.common.abstract_device import DeviceDescriptor from modules.common.abstract_io import AbstractIoAction +from modules.common.utils.component_parser import get_io_name_by_id +from modules.io_actions.common import check_fault_state_io_device from modules.io_actions.controllable_consumers.ripple_control_receiver.config import RippleControlReceiverSetup control_command_log = logging.getLogger("steuve_control_command") @@ -16,40 +20,53 @@ def __init__(self, config: RippleControlReceiverSetup): super().__init__() def setup(self) -> None: - with ModifyLoglevelContext(control_command_log, logging.DEBUG): - for pattern in self.config.configuration.input_pattern: - for digital_input, value in pattern["matrix"].items(): - if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ - digital_input] != value: - break - else: - # Alle digitalen Eingänge entsprechen dem Pattern - if pattern["value"] != 1: - if self.timestamp is None: - Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) - control_command_log.info( - f"RSE-Sperre mit Wert {pattern['value']*100}" - "% aktiviert. Leistungswerte vor Ausführung des Steuerbefehls:") + def log_active_ripple_control_receiver(): + if self.timestamp is None: + Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) + if check_fault_state_io_device(self.config.configuration.io_device): + control_command_log.info( + "Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.") + control_command_log.info( + f"RSE-Sperre mit Wert {pattern['value']*100}" + "% aktiviert. Leistungswerte vor Ausführung des Steuerbefehls:") + + evu_counter = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()] + msg = f"EVU-Zähler: {evu_counter.data.get.powers}W" + for device in self.config.configuration.devices: + if device["type"] == "cp": + cp = f"cp{device['id']}" + msg += (f", Ladepunkt {data.data.cp_data[cp].data.config.name}: " + f"{data.data.cp_data[cp].data.get.powers}W") + if device["type"] == "io": + io = f"io{device['id']}" + msg += (f", IO-Gerät {data.data.io_data[io].data.config.name}: " + "Leistung unbekannt") + control_command_log.info(msg) - evu_counter = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()] - msg = f"EVU-Zähler: {evu_counter.data.get.powers}W" - for device in self.config.configuration.devices: - if device["type"] == "cp": - cp = f"cp{device['id']}" - msg += (f", Ladepunkt {data.data.cp_data[cp].data.config.name}: " - f"{data.data.cp_data[cp].data.get.powers}W") - if device["type"] == "io": - io = f"io{device['id']}" - msg += (f", IO-Gerät {data.data.io_data[io].data.config.name}: " - "Leistung unbekannt") - control_command_log.info(msg) - break + with ModifyLoglevelContext(control_command_log, logging.DEBUG): + if check_fault_state_io_device(self.config.configuration.io_device): + log_active_ripple_control_receiver() + for pattern in self.config.configuration.input_pattern: + for digital_input, value in pattern["matrix"].items(): + if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ + digital_input] != value: + break + else: + # Alle digitalen Eingänge entsprechen dem Pattern + if pattern["value"] != 1: + log_active_ripple_control_receiver() + break else: if self.timestamp: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None) control_command_log.info("RSE-Sperre deaktiviert.") - def ripple_control_receiver(self) -> float: + def ripple_control_receiver(self) -> Tuple[float, LoadmanagementLimit]: + if check_fault_state_io_device(self.config.configuration.io_device): + return (0, LoadmanagementLimit( + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( + self.config.configuration.io_device)), + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR)) for pattern in self.config.configuration.input_pattern: for digital_input, value in pattern["matrix"].items(): if data.data.io_states[f"io_states{self.config.configuration.io_device}" @@ -57,10 +74,21 @@ def ripple_control_receiver(self) -> float: break else: # Alle digitalen Eingänge entsprechen dem Pattern - return pattern["value"] if pattern["value"] is not None else 0 + if pattern["value"] is None: + return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIFGURATION, + LimitingValue.MISSING_CONFIFGURATION) + if pattern["value"] != 1: + limit = LoadmanagementLimit( + LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(pattern["value"]*100), + LimitingValue.RIPPLE_CONTROL_RECEIVER) + else: + limit = LoadmanagementLimit(None, None) + return pattern["value"], limit else: # Zustand entspricht keinem Pattern - return 0 + return 0, LoadmanagementLimit( + LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(0), + LimitingValue.RIPPLE_CONTROL_RECEIVER) def create_action(config: RippleControlReceiverSetup, parent_device_type: str): diff --git a/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py b/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py index df60a75eef..e4971c7f6b 100644 --- a/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py +++ b/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py @@ -1,12 +1,14 @@ import logging from typing import Optional from control import data +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.logger import ModifyLoglevelContext from helpermodules.pub import Pub from helpermodules.timecheck import create_timestamp from modules.common.abstract_device import DeviceDescriptor from modules.common.abstract_io import AbstractIoAction -from modules.common.utils.component_parser import get_component_name_by_id +from modules.common.utils.component_parser import get_component_name_by_id, get_io_name_by_id +from modules.io_actions.common import check_fault_state_io_device from modules.io_actions.generator_systems.stepwise_control.config import StepwiseControlSetup from modules.io_devices.eebus.config import AnalogInputMapping, DigitalInputMapping @@ -86,17 +88,33 @@ def setup(self) -> None: control_command_log.info("EZA-Begrenzung aufgehoben.") def control_stepwise(self) -> Optional[float]: + if check_fault_state_io_device(self.config.configuration.io_device): + return (0, LoadmanagementLimit( + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( + self.config.configuration.io_device)), + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR)) for pattern in self.config.configuration.input_pattern: for digital_input, value in pattern["matrix"].items(): if data.data.io_states[f"io_states{self.config.configuration.io_device}" ].data.get.digital_input[digital_input] != value: break else: + if pattern["value"] is None: + return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIFGURATION, + LimitingValue.MISSING_CONFIFGURATION) # Alle digitalen Eingänge entsprechen dem Pattern - return pattern['value'] + elif pattern["value"] != 1: + limit = LoadmanagementLimit( + LimitingValue.CONTROL_STEPWISE.value.format(pattern["value"]*100), + LimitingValue.CONTROL_STEPWISE) + else: + limit = LoadmanagementLimit("Keine Leistungsbegrenzung aktiv.", "Keine Leistungsbegrenzung aktiv.") + return pattern["value"], limit else: - # Zustand entspricht keinem Pattern, Leistungsbegrenzung aufheben - return 1 + # Zustand entspricht keinem Pattern + return 0, LoadmanagementLimit( + LimitingValue.CONTROL_STEPWISE.value.format(0), + LimitingValue.CONTROL_STEPWISE) def create_action(config: StepwiseControlSetup, parent_device_type: str): diff --git a/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py b/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py index f1108eeb5d..10472bb170 100644 --- a/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py +++ b/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py @@ -1,12 +1,14 @@ import logging -from typing import Optional +from typing import Optional, Tuple from control import data +from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.logger import ModifyLoglevelContext from helpermodules.pub import Pub from helpermodules.timecheck import create_timestamp from modules.common.abstract_device import DeviceDescriptor from modules.common.abstract_io import AbstractIoAction -from modules.common.utils.component_parser import get_component_name_by_id +from modules.common.utils.component_parser import get_component_name_by_id, get_io_name_by_id +from modules.io_actions.common import check_fault_state_io_device from modules.io_actions.generator_systems.stepwise_control.config import StepwiseControlSetup control_command_log = logging.getLogger("steuve_control_command") @@ -79,18 +81,34 @@ def setup(self) -> None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None) control_command_log.info("EZA-Begrenzung aufgehoben.") - def control_stepwise(self) -> Optional[float]: + def control_stepwise(self) -> Tuple[Optional[float], LoadmanagementLimit]: + if check_fault_state_io_device(self.config.configuration.io_device): + return (0, LoadmanagementLimit( + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( + self.config.configuration.io_device)), + LimitingValue.CONTROLLABLE_CONSUMERS_ERROR)) for pattern in self.config.configuration.input_pattern: for digital_input, value in pattern["matrix"].items(): if data.data.io_states[f"io_states{self.config.configuration.io_device}" ].data.get.digital_input[digital_input] != value: break else: + if pattern["value"] is None: + return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIFGURATION, + LimitingValue.MISSING_CONFIFGURATION) # Alle digitalen Eingänge entsprechen dem Pattern - return pattern['value'] + elif pattern["value"] != 1: + limit = LoadmanagementLimit( + LimitingValue.CONTROL_STEPWISE.value.format(pattern["value"]*100), + LimitingValue.CONTROL_STEPWISE) + else: + limit = LoadmanagementLimit("Keine Leistungsbegrenzung aktiv.", "Keine Leistungsbegrenzung aktiv.") + return pattern["value"], limit else: - # Zustand entspricht keinem Pattern, Leistungsbegrenzung aufheben - return 1 + # Zustand entspricht keinem Pattern + return 0, LoadmanagementLimit( + LimitingValue.CONTROL_STEPWISE.value.format(0), + LimitingValue.CONTROL_STEPWISE) def create_action(config: StepwiseControlSetup, parent_device_type: str): From 4cbf649da3f7361fb96784288b81467993fc31ca Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 11 Mar 2026 14:49:13 +0100 Subject: [PATCH 2/3] fixes --- packages/control/io_device.py | 12 +----------- packages/control/limiting_value.py | 4 ++-- packages/control/loadmanagement.py | 2 ++ .../controllable_consumers/dimming/api_eebus.py | 4 ++-- .../controllable_consumers/dimming/api_io.py | 4 ++-- .../dimming_direct_control/api.py | 9 +++++---- .../ripple_control_receiver/api.py | 7 ++++--- .../generator_systems/stepwise_control/api_eebus.py | 5 +++-- .../generator_systems/stepwise_control/api_io.py | 2 ++ 9 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/control/io_device.py b/packages/control/io_device.py index c351002901..b24138a75b 100644 --- a/packages/control/io_device.py +++ b/packages/control/io_device.py @@ -1,9 +1,7 @@ from dataclasses import dataclass, field from typing import Dict, Optional, Tuple, Union -from control import data -from control.limiting_value import LimitingValue, LoadmanagementLimit +from control.limiting_value import LoadmanagementLimit from helpermodules.constants import NO_ERROR -from modules.common.utils.component_parser import get_io_name_by_id from modules.io_actions.controllable_consumers.dimming.api_eebus import DimmingEebus from modules.io_actions.controllable_consumers.dimming.api_io import DimmingIo from modules.io_actions.controllable_consumers.dimming_direct_control.api import DimmingDirectControl @@ -63,16 +61,11 @@ def setup(self): for action in self.actions.values(): action.setup() - def _check_fault_state_io_device(self, io_device: int) -> None: - if data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2: - raise ValueError(LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(io_device))) - def dimming_get_import_power_left(self, device: Dict) -> Tuple[Optional[float], LoadmanagementLimit]: for action in self.actions.values(): if isinstance(action, (DimmingIo, DimmingEebus)): for d in action.config.configuration.devices: if device == d: - self._check_fault_state_io_device(action.config.configuration.io_device) return action.dimming_get_import_power_left() else: return None, LoadmanagementLimit(None, None) @@ -89,7 +82,6 @@ def dimming_via_direct_control(self, device: Dict) -> Tuple[Optional[float], Loa if isinstance(action, DimmingDirectControl): for d in action.config.configuration.devices: if device == d: - self._check_fault_state_io_device(action.config.configuration.io_device) return action.dimming_via_direct_control() else: return None, LoadmanagementLimit(None, None) @@ -99,7 +91,6 @@ def ripple_control_receiver(self, device: Dict) -> Tuple[float, LoadmanagementLi if isinstance(action, RippleControlReceiver): for d in action.config.configuration.devices: if device == d: - self._check_fault_state_io_device(action.config.configuration.io_device) return action.ripple_control_receiver() else: return 1, LoadmanagementLimit(None, None) @@ -108,7 +99,6 @@ def stepwise_control(self, device_id: int) -> Tuple[Optional[float], Loadmanagem for action in self.actions.values(): if isinstance(action, (StepwiseControlEebus, StepwiseControlIo)): if device_id in [component["id"] for component in action.config.configuration.devices]: - self._check_fault_state_io_device(action.config.configuration.io_device) return action.control_stepwise() else: return None, LoadmanagementLimit(None, None) diff --git a/packages/control/limiting_value.py b/packages/control/limiting_value.py index 558e76e44a..9fcacef4d9 100644 --- a/packages/control/limiting_value.py +++ b/packages/control/limiting_value.py @@ -12,8 +12,8 @@ class LimitingValue(Enum): RIPPLE_CONTROL_RECEIVER = (", da der Ladepunkt durch den RSE-Kontakt auf {}% der konfigurierten Anschlussleistung " "reduziert wird.") CONTROL_STEPWISE = "Leistung begrenzt auf {}%" - CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nicht " - "gesteuert werden können. Bitte prüfe die Status-Seite.") + CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nur " + "mit der minimalen Leistung betrieben werden können. Bitte prüfe die Status-Seite.") MISSING_CONFIFGURATION = ", da die Konfiguration für die Aktion unvollständig ist." diff --git a/packages/control/loadmanagement.py b/packages/control/loadmanagement.py index 911d149e9f..4853171b98 100644 --- a/packages/control/loadmanagement.py +++ b/packages/control/loadmanagement.py @@ -144,6 +144,8 @@ def _limit_by_dimming_via_direct_control(self, available_currents = [current_per_phase - cp.data.set.target_current if c > 0 else 0 for c in missing_currents] log.debug(f"Dimmung per Direkt-Steuerung: {available_currents}A") + else: + available_currents = missing_currents return available_currents, limit def _limit_by_dimming(self, diff --git a/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py b/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py index 8a1bd51579..1679fdf6c3 100644 --- a/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py +++ b/packages/modules/io_actions/controllable_consumers/dimming/api_eebus.py @@ -55,8 +55,8 @@ def setup(self) -> None: control_command_log.info(f"Dimmen aktiviert. Übermittelter LPC-Wert: {lpc_value/1000}kWh. " "Leistungswerte vor Ausführung des Steuerbefehls:") - msg = (f"EVU-Zähler: " - f"{data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()].data.get.powers}W") + evu_counter = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()] + msg = f"EVU-Zähler: {evu_counter.data.get.powers}W, {evu_counter.data.get.power}W" for device in self.config.configuration.devices: if device["type"] == "cp": cp = f"cp{device['id']}" diff --git a/packages/modules/io_actions/controllable_consumers/dimming/api_io.py b/packages/modules/io_actions/controllable_consumers/dimming/api_io.py index 81a77b5859..bfb3bd6802 100644 --- a/packages/modules/io_actions/controllable_consumers/dimming/api_io.py +++ b/packages/modules/io_actions/controllable_consumers/dimming/api_io.py @@ -60,8 +60,8 @@ def setup(self) -> None: control_command_log.info("Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.") control_command_log.info("Dimmen aktiviert. Leistungswerte vor Ausführung des Steuerbefehls:") - msg = (f"EVU-Zähler: " - f"{data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()].data.get.powers}W") + evu_counter = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()] + msg = f"EVU-Zähler: {evu_counter.data.get.powers}W, {evu_counter.data.get.power}W" for device in self.config.configuration.devices: if device["type"] == "cp": cp = f"cp{device['id']}" diff --git a/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py b/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py index 6672e9d2c8..22a821e819 100644 --- a/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py +++ b/packages/modules/io_actions/controllable_consumers/dimming_direct_control/api.py @@ -32,8 +32,9 @@ def __init__(self, config: DimmingDirectControlSetup): def setup(self) -> None: with ModifyLoglevelContext(control_command_log, logging.DEBUG): - if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ - self.dimming_input] == self.dimming_value or check_fault_state_io_device(self.config.configuration.io_device): + if (data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ + self.dimming_input] == self.dimming_value or + check_fault_state_io_device(self.config.configuration.io_device)): device = self.config.configuration.devices[0] if device["type"] == "cp": cp = f"cp{device['id']}" @@ -48,8 +49,8 @@ def setup(self) -> None: f"{data.data.cp_data[cp].data.config.name} aktiviert. " "Leistungswerte vor Ausführung des Steuerbefehls:") - msg = (f"EVU-Zähler: " - f"{data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()].data.get.powers}W") + evu_counter = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()] + msg = f"EVU-Zähler: {evu_counter.data.get.powers}W, {evu_counter.data.get.power}W" if device["type"] == "cp": msg += (f", Ladepunkt {data.data.cp_data[cp].data.config.name}: " f"{data.data.cp_data[cp].data.get.powers}W") diff --git a/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py b/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py index 157e805882..7bf95a11a3 100644 --- a/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py +++ b/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py @@ -25,13 +25,13 @@ def log_active_ripple_control_receiver(): Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) if check_fault_state_io_device(self.config.configuration.io_device): control_command_log.info( - "Fehler des IO-Geräts: Dimmen aktiviert für Failsafe-Modus.") + "Fehler des IO-Geräts: RSE aktiviert für Failsafe-Modus.") control_command_log.info( f"RSE-Sperre mit Wert {pattern['value']*100}" "% aktiviert. Leistungswerte vor Ausführung des Steuerbefehls:") evu_counter = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()] - msg = f"EVU-Zähler: {evu_counter.data.get.powers}W" + msg = f"EVU-Zähler: {evu_counter.data.get.powers}W, {evu_counter.data.get.power}W" for device in self.config.configuration.devices: if device["type"] == "cp": cp = f"cp{device['id']}" @@ -48,7 +48,8 @@ def log_active_ripple_control_receiver(): log_active_ripple_control_receiver() for pattern in self.config.configuration.input_pattern: for digital_input, value in pattern["matrix"].items(): - if data.data.io_states[f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ + if data.data.io_states[ + f"io_states{self.config.configuration.io_device}"].data.get.digital_input[ digital_input] != value: break else: diff --git a/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py b/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py index e4971c7f6b..d0c6cf8d88 100644 --- a/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py +++ b/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py @@ -58,7 +58,7 @@ def setup(self) -> None: for inverter in self.config.configuration.devices: max_output_inverter += data.data.pv_data[f"pv{inverter['id']}"].data.config.max_ac_out - if self.lpp_active: + if self.lpp_active or check_fault_state_io_device(self.config.configuration.io_device): try: self.step = self.lpp_value / max_output_inverter except ZeroDivisionError: @@ -71,7 +71,8 @@ def setup(self) -> None: if self.step <= s: self.step = s break - + if check_fault_state_io_device(self.config.configuration.io_device): + control_command_log.info("Fehler des IO-Geräts: EZA-Begrenzung kann nicht erfasst werden.") if changed: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp()) control_command_log.info(f"EEBus-Steuerung: LPP-Wert {self.lpp_value} / " diff --git a/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py b/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py index 10472bb170..f0627f921c 100644 --- a/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py +++ b/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py @@ -59,6 +59,8 @@ def setup(self) -> None: if digital_input[input_name] != digital_input_prev[input_name] ]) > 0 + if check_fault_state_io_device(self.config.configuration.io_device): + control_command_log.info("Fehler des IO-Geräts: EZA-Begrenzung kann nicht erfasst werden.") for pattern in self.config.configuration.input_pattern: for action_input, value in pattern["matrix"].items(): if digital_input[action_input] != value: From dcdafd5e71ecd608202953abaf4e1a592b11666f Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 11 Mar 2026 15:39:15 +0100 Subject: [PATCH 3/3] review --- packages/control/limiting_value.py | 2 +- packages/control/loadmanagement.py | 2 +- packages/control/process.py | 4 ++-- packages/modules/io_actions/common.py | 3 ++- .../ripple_control_receiver/api.py | 5 ++--- .../generator_systems/stepwise_control/api_eebus.py | 10 +++++----- .../generator_systems/stepwise_control/api_io.py | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/control/limiting_value.py b/packages/control/limiting_value.py index 9fcacef4d9..e4b32a16cf 100644 --- a/packages/control/limiting_value.py +++ b/packages/control/limiting_value.py @@ -14,7 +14,7 @@ class LimitingValue(Enum): CONTROL_STEPWISE = "Leistung begrenzt auf {}%" CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nur " "mit der minimalen Leistung betrieben werden können. Bitte prüfe die Status-Seite.") - MISSING_CONFIFGURATION = ", da die Konfiguration für die Aktion unvollständig ist." + MISSING_CONFIGURATION = ", da die Konfiguration für die Aktion unvollständig ist." @dataclass diff --git a/packages/control/loadmanagement.py b/packages/control/loadmanagement.py index 4853171b98..59a56d174c 100644 --- a/packages/control/loadmanagement.py +++ b/packages/control/loadmanagement.py @@ -172,7 +172,7 @@ def _limit_by_ripple_control_receiver(self, max_current = cp.template.data.max_current_multi_phases # target_current ist das Ergebnis der letzten Iteration. Die Differenz der begrenzten Anschlussleistung und # der Sollstrom der letzten Iteration dürfen daher nicht größer sein als der aktuell fehlende Strom. - available_currents = [min(max_current*value - cp.data.set.target_current, c) + available_currents = [max(min(max_current*value - cp.data.set.target_current, c), 0) if c > 0 else 0 for c in available_currents] log.debug(f"Reduzierung durch RSE-Kontakt auf {value*100}%, maximal {max_current*value}A") return available_currents, limit diff --git a/packages/control/process.py b/packages/control/process.py index 78b95ed667..fdda08c128 100644 --- a/packages/control/process.py +++ b/packages/control/process.py @@ -72,7 +72,7 @@ def process_algorithm_results(self) -> None: for d in action.config.configuration.devices: if d["type"] == "io": data.data.io_states[f"io_states{d['id']}"].data.set.digital_output[d["digital_output"]] = ( - action.dimming_via_direct_control() is None # active output (True) if no dimming + action.dimming_via_direct_control()[0] is None # active output (True) if no dimming ) if isinstance(action, DimmingIo): for d in action.config.configuration.devices: @@ -85,7 +85,7 @@ def process_algorithm_results(self) -> None: if action.config.configuration.passthrough_enabled: # find output pattern by value for pattern in action.config.configuration.output_pattern: - if pattern["value"] == action.control_stepwise(): + if pattern["value"] == action.control_stepwise()[0]: # set digital outputs according to matching output_pattern for output in pattern["matrix"].keys(): data.data.io_states[ diff --git a/packages/modules/io_actions/common.py b/packages/modules/io_actions/common.py index 126b773d2f..fc448436a6 100644 --- a/packages/modules/io_actions/common.py +++ b/packages/modules/io_actions/common.py @@ -1,5 +1,6 @@ from control import data +from modules.common.fault_state_level import FaultStateLevel def check_fault_state_io_device(io_device: int) -> bool: - return data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2 + return data.data.io_states[f"io_states{io_device}"].data.get.fault_state == FaultStateLevel.ERROR diff --git a/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py b/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py index 7bf95a11a3..5fd6b787bb 100644 --- a/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py +++ b/packages/modules/io_actions/controllable_consumers/ripple_control_receiver/api.py @@ -45,7 +45,6 @@ def log_active_ripple_control_receiver(): with ModifyLoglevelContext(control_command_log, logging.DEBUG): if check_fault_state_io_device(self.config.configuration.io_device): - log_active_ripple_control_receiver() for pattern in self.config.configuration.input_pattern: for digital_input, value in pattern["matrix"].items(): if data.data.io_states[ @@ -76,8 +75,8 @@ def ripple_control_receiver(self) -> Tuple[float, LoadmanagementLimit]: else: # Alle digitalen Eingänge entsprechen dem Pattern if pattern["value"] is None: - return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIFGURATION, - LimitingValue.MISSING_CONFIFGURATION) + return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIGURATION.value, + LimitingValue.MISSING_CONFIGURATION) if pattern["value"] != 1: limit = LoadmanagementLimit( LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(pattern["value"]*100), diff --git a/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py b/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py index d0c6cf8d88..0219de97f6 100644 --- a/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py +++ b/packages/modules/io_actions/generator_systems/stepwise_control/api_eebus.py @@ -1,5 +1,5 @@ import logging -from typing import Optional +from typing import Optional, Tuple from control import data from control.limiting_value import LimitingValue, LoadmanagementLimit from helpermodules.logger import ModifyLoglevelContext @@ -88,7 +88,7 @@ def setup(self) -> None: Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None) control_command_log.info("EZA-Begrenzung aufgehoben.") - def control_stepwise(self) -> Optional[float]: + def control_stepwise(self) -> Tuple[Optional[float], LoadmanagementLimit]: if check_fault_state_io_device(self.config.configuration.io_device): return (0, LoadmanagementLimit( LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id( @@ -101,15 +101,15 @@ def control_stepwise(self) -> Optional[float]: break else: if pattern["value"] is None: - return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIFGURATION, - LimitingValue.MISSING_CONFIFGURATION) + return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIGURATION.value, + LimitingValue.MISSING_CONFIGURATION) # Alle digitalen Eingänge entsprechen dem Pattern elif pattern["value"] != 1: limit = LoadmanagementLimit( LimitingValue.CONTROL_STEPWISE.value.format(pattern["value"]*100), LimitingValue.CONTROL_STEPWISE) else: - limit = LoadmanagementLimit("Keine Leistungsbegrenzung aktiv.", "Keine Leistungsbegrenzung aktiv.") + limit = LoadmanagementLimit("Keine Leistungsbegrenzung aktiv.", None) return pattern["value"], limit else: # Zustand entspricht keinem Pattern diff --git a/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py b/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py index f0627f921c..a9a04a2167 100644 --- a/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py +++ b/packages/modules/io_actions/generator_systems/stepwise_control/api_io.py @@ -96,15 +96,15 @@ def control_stepwise(self) -> Tuple[Optional[float], LoadmanagementLimit]: break else: if pattern["value"] is None: - return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIFGURATION, - LimitingValue.MISSING_CONFIFGURATION) + return 0, LoadmanagementLimit(LimitingValue.MISSING_CONFIGURATION.value, + LimitingValue.MISSING_CONFIGURATION) # Alle digitalen Eingänge entsprechen dem Pattern elif pattern["value"] != 1: limit = LoadmanagementLimit( LimitingValue.CONTROL_STEPWISE.value.format(pattern["value"]*100), LimitingValue.CONTROL_STEPWISE) else: - limit = LoadmanagementLimit("Keine Leistungsbegrenzung aktiv.", "Keine Leistungsbegrenzung aktiv.") + limit = LoadmanagementLimit("Keine Leistungsbegrenzung aktiv.", None) return pattern["value"], limit else: # Zustand entspricht keinem Pattern