Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/helpermodules/subdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,8 @@ def process_system_topic(self, client: mqtt.Client, var: dict, msg: mqtt.MQTTMes
index = get_index(msg.topic)
if decode_payload(msg.payload) == "":
if "io"+index in var:
if var[f"io{index}"].config.configuration.host == "localhost":
if (var[f"io{index}"].config.type == "add_on" and
var[f"io{index}"].config.configuration.host == "localhost"):
var.pop("iolocal")
var.pop("io"+index)
else:
Expand Down
4 changes: 4 additions & 0 deletions packages/modules/common/version_by_telnet.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import socket
import telnetlib


def get_version_by_telnet(expected_bytearray: str, ip_address: str, port: int = 8898):
if ip_address is None:
# None liefert einen ValueError genauso wie wenn telnet nicht aktiv ist.
raise socket.timeout("Die IP-Adresse ist nicht erreichbar. Bitte überprüfe die Einstellungen.")
# telnetlib ist ab Python 3.11 deprecated
with telnetlib.Telnet(ip_address, port, 2) as client:
answer = client.read_until(bytearray(expected_bytearray, 'utf-8'), 2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,30 @@ def __init__(self, config: DimmingSetup):
super().__init__()

def setup(self) -> None:
lpc_value = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input[AnalogInputMapping.LPC_VALUE.name]
surplus = data.data.counter_data[data.data.counter_all_data.get_evu_counter_str()].calc_raw_surplus()
if surplus > 0:
self.import_power_left = lpc_value + surplus

if check_fault_state_io_device(self.config.configuration.io_device):
self.import_power_left = surplus
else:
self.import_power_left = lpc_value
self.import_power_left -= self.config.configuration.fixed_import_power
lpc_value = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input[AnalogInputMapping.LPC_VALUE.name]
if surplus > 0:
self.import_power_left = lpc_value + surplus
else:
self.import_power_left = lpc_value
self.import_power_left = max(0, self.import_power_left - self.config.configuration.fixed_import_power)
log.debug(f"Dimmen: {self.import_power_left}W inkl. Überschuss")

with ModifyLoglevelContext(control_command_log, logging.DEBUG):
if self.dimming_active() or check_fault_state_io_device(self.config.configuration.io_device):
if check_fault_state_io_device(self.config.configuration.io_device) or self.dimming_active():
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.")
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:")
if check_fault_state_io_device(self.config.configuration.io_device) is False:
control_command_log.info(f"Dimmen aktiviert. Übermittelter LPC-Wert: {lpc_value/1000}kWh. "
"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, {evu_counter.data.get.power}W"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ 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() or check_fault_state_io_device(self.config.configuration.io_device):
if check_fault_state_io_device(self.config.configuration.io_device) or self.dimming_active():
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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,49 +44,50 @@ def __init__(self, config: StepwiseControlSetup):

def setup(self) -> None:
with ModifyLoglevelContext(control_command_log, logging.DEBUG):
self.lpp_value = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input[AnalogInputMapping.LPP_VALUE.name]
lpp_value_prev = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input_prev[AnalogInputMapping.LPP_VALUE.name]
self.lpp_active = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.digital_input[DigitalInputMapping.LPP_ACTIVE.name]
lpp_active_prev = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.digital_input_prev[DigitalInputMapping.LPP_ACTIVE.name]
changed = True if self.lpp_value != lpp_value_prev or self.lpp_active != lpp_active_prev else False
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.")
else:
self.lpp_value = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input[AnalogInputMapping.LPP_VALUE.name]
lpp_value_prev = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input_prev[AnalogInputMapping.LPP_VALUE.name]
self.lpp_active = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.digital_input[DigitalInputMapping.LPP_ACTIVE.name]
lpp_active_prev = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.digital_input_prev[DigitalInputMapping.LPP_ACTIVE.name]
changed = True if self.lpp_value != lpp_value_prev or self.lpp_active != lpp_active_prev else False

max_output_inverter = 0
for inverter in self.config.configuration.devices:
max_output_inverter += data.data.pv_data[f"pv{inverter['id']}"].data.config.max_ac_out
max_output_inverter = 0
for inverter in self.config.configuration.devices:
max_output_inverter += data.data.pv_data[f"pv{inverter['id']}"].data.config.max_ac_out
Comment on lines +61 to +62
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In max_output_inverter calculation you iterate over all self.config.configuration.devices and unconditionally access pv_data[f"pv{inverter['id']}"]. devices can also contain non-inverter entries (e.g. IO outputs), which would make this lookup invalid and can raise a KeyError or sum unrelated IDs. Filter the list to device["type"] == "inverter" (and only then access pv_data).

Suggested change
for inverter in self.config.configuration.devices:
max_output_inverter += data.data.pv_data[f"pv{inverter['id']}"].data.config.max_ac_out
for device in self.config.configuration.devices:
if device["type"] == "inverter":
max_output_inverter += data.data.pv_data[f"pv{device['id']}"].data.config.max_ac_out

Copilot uses AI. Check for mistakes.

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:
msg = ("Bitte unter Konfiguration -> Lastmanagement die maximale Ausgangsleistung"
" des Wechselrichters angeben.")
log.exception(msg)
control_command_log.info(msg)
self.step = 0
for s in [0, 0.25, 0.5, 0.75, 1.0]:
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} / "
f"max. PV-Leistung {max_output_inverter} = {self.step}")
control_command_log.info(f"EZA-Begrenzung mit Wert {self.step*100}% aktiviert.")
for device in self.config.configuration.devices:
control_command_log.info(
f"Erzeugungsanlage {get_component_name_by_id(device)} "
f"auf {self.step*100}% begrenzt."
)
else:
if changed:
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
control_command_log.info("EZA-Begrenzung aufgehoben.")
if self.lpp_active:
try:
self.step = self.lpp_value / max_output_inverter
except ZeroDivisionError:
msg = ("Bitte unter Konfiguration -> Lastmanagement die maximale Ausgangsleistung"
" des Wechselrichters angeben.")
log.exception(msg)
control_command_log.info(msg)
self.step = 0
for s in [0, 0.25, 0.5, 0.75, 1.0]:
if self.step <= s:
self.step = s
break
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} / "
f"max. PV-Leistung {max_output_inverter} = {self.step}")
control_command_log.info(f"EZA-Begrenzung mit Wert {self.step*100}% aktiviert.")
for device in self.config.configuration.devices:
control_command_log.info(
f"Erzeugungsanlage {get_component_name_by_id(device['id'])} "
f"auf {self.step*100}% begrenzt."
)
else:
if changed:
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
control_command_log.info("EZA-Begrenzung aufgehoben.")

def control_stepwise(self) -> Tuple[Optional[float], LoadmanagementLimit]:
if check_fault_state_io_device(self.config.configuration.io_device):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,41 +47,43 @@ def __init__(self, config: StepwiseControlSetup):

def setup(self) -> None:
with ModifyLoglevelContext(control_command_log, logging.DEBUG):
digital_input = (
data.data.io_states[
f"io_states{self.config.configuration.io_device}"
].data.get.digital_input
)
digital_input_prev = data.data.io_states[
f"io_states{self.config.configuration.io_device}"].data.get.digital_input_prev
changed = len([
input_name for input_name in self.__unique_inputs
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:
break
else:
# Alle digitalen Eingänge entsprechen dem Pattern
if pattern["value"] != 1:
if changed:
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp())
control_command_log.info(f"EZA-Begrenzung mit Wert {int(pattern['value']*100)}% aktiviert.")
for device in self.config.configuration.devices:
if device["type"] == "inverter":
control_command_log.info(
f"Erzeugungsanlage {get_component_name_by_id(device['id'])} "
f"auf {int(pattern['value']*100)}% begrenzt."
)
break
else:
if changed:
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
control_command_log.info("EZA-Begrenzung aufgehoben.")
digital_input = (
data.data.io_states[
f"io_states{self.config.configuration.io_device}"
].data.get.digital_input
)
digital_input_prev = data.data.io_states[
f"io_states{self.config.configuration.io_device}"].data.get.digital_input_prev
changed = len([
input_name for input_name in self.__unique_inputs
if digital_input[input_name] != digital_input_prev[input_name]
]) > 0

for pattern in self.config.configuration.input_pattern:
for action_input, value in pattern["matrix"].items():
if digital_input[action_input] != value:
break
else:
# Alle digitalen Eingänge entsprechen dem Pattern
if pattern["value"] != 1:
if changed:
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", create_timestamp())
control_command_log.info(
f"EZA-Begrenzung mit Wert {int(pattern['value']*100)}% aktiviert.")
for device in self.config.configuration.devices:
if device["type"] == "inverter":
control_command_log.info(
f"Erzeugungsanlage {get_component_name_by_id(device['id'])} "
f"auf {int(pattern['value']*100)}% begrenzt."
)
break
else:
if changed:
Pub().pub(f"openWB/set/io/action/{self.config.id}/timestamp", None)
control_command_log.info("EZA-Begrenzung aufgehoben.")

def control_stepwise(self) -> Tuple[Optional[float], LoadmanagementLimit]:
if check_fault_state_io_device(self.config.configuration.io_device):
Expand Down
Loading