Skip to content

Add LoadManager#3636

Draft
AlexanderHa98 wants to merge 12 commits into
openWB:masterfrom
AlexanderHa98:feature_LoadManager
Draft

Add LoadManager#3636
AlexanderHa98 wants to merge 12 commits into
openWB:masterfrom
AlexanderHa98:feature_LoadManager

Conversation

@AlexanderHa98

@AlexanderHa98 AlexanderHa98 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

openWB/openwb-ui-settings#1007

Aufbau

GUI

  • Eingabe der maximalen Bezugsleistung im Fehlerfall
  • Eingabe des maximal zulässigen Stroms (Ampere) im Fehlerfall
  • Auswahl der zu verwendenden Ladepunkte

MQTT-Topic

openWB/set/mqtt/loadmanager/set/{id}/loadmanager

Beispiel-Payload

{
  "max_power": 1000,
  "max_current": [6, 6, 6],
  "timestamp": 1783596593.635847
}

Funktionsweise

Lastmanagement

Der Lastmanager berechnet die verbleibend verfügbare Leistung und den maximal zulässigen Strom.

Dabei erfolgt die Begrenzung in folgender Reihenfolge:

  1. Prüfung der Leistungsgrenze (über MQTT vorgegeben max_power).

    • Ist die Grenze erreicht oder überschritten, wird die Leistung entsprechend reduziert.
  2. Prüfung der Stromgrenze (über MQTT vorgegeben max_current).

    • Ist die Grenze erreicht oder überschritten, wird der Strom entsprechend reduziert.
  3. Sind weder Leistungs- noch Stromgrenze erreicht, wird keine Reduzierung durchgeführt.

Fehlerfall

Wenn der timestamp älter als 60 Sekunden ist:

  • Es wird eine RuntimeError ausgelöst.
  • Es werden die konfigurierte maximale Bezugsleistung und die maximalen Amperewerte für den Fehlerfall verwendet.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new “LoadManager” integration that ingests import-limit data via MQTT as an IO device and applies it as an additional limiting step in the charging loadmanagement pipeline.

Changes:

  • Introduces a new MQTT-based IO device module for LoadManager analog inputs (max power/current/timestamp).
  • Adds a new controllable-consumers action (DimmingLoadManager) and wires it into loadmanagement limiting.
  • Extends MQTT set-topic validation to include the new loadmanager topic.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/modules/io_devices/load_manager/config.py Defines LoadManager IO device config and analog input mapping.
packages/modules/io_devices/load_manager/api.py Subscribes to internal MQTT topics and populates IoState analog inputs for the LoadManager.
packages/modules/io_devices/load_manager/init.py Module package initializer for the new IO device.
packages/modules/io_actions/controllable_consumers/load_manager/config.py Adds configuration/setup types for the new controllable-consumers LoadManager action.
packages/modules/io_actions/controllable_consumers/load_manager/api.py Implements DimmingLoadManager action that provides remaining import power for loadmanagement.
packages/helpermodules/setdata.py Validates the new openWB/set/mqtt/loadmanager/... topic so it is forwarded to internal MQTT.
packages/control/loadmanagement.py Adds a new _limit_loadmanager limiting step in get_available_currents.
packages/control/io_device.py Registers the new IO action type and exposes it via IoActions.dimming_load_manager().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/control/io_device.py Outdated
Comment thread packages/control/loadmanagement.py Outdated
Comment on lines +191 to +198
dimming_power_left, limit = data.data.io_actions.dimming_load_manager({"type": "cp", "id": cp.num})
if dimming_power_left is not None:
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, limit
Comment on lines +36 to +42
def setup(self) -> None:
if check_fault_state_io_device(self.config.configuration.io_device):
self.import_power_left = self.config.configuration.fixed_import_power
else:
max_power = data.data.io_states[f"io_states{self.config.configuration.io_device}"
].data.get.analog_input[AnalogInputMapping.MAX_POWER.name]
self.import_power_left = max_power
Comment thread packages/helpermodules/setdata.py Outdated
return Path(__file__).resolve().parents[2]/"ramdisk"

def loadmanager_topic(self, msg: mqtt.MQTTMessage):
""" Handler für die LoadMananger-Topics
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.

Comment on lines +33 to +41
self.name = name
self.type = type
self.id = id
self.configuration = configuration or LoadManagerConfiguration()
if input is None:
input = init_input()
if output is None:
output = init_output()
super().__init__(name, type, id, configuration or LoadManagerConfiguration(), input=input, output=output)
broker = BrokerClient("subscribeMqttLoadmanager",
on_connect, on_message)

return ConfigurableIo(config=config, component_reader=read, component_writer=lambda: None, initializer=initializer)
Comment thread packages/helpermodules/setdata.py Outdated
return Path(__file__).resolve().parents[2]/"ramdisk"

def loadmanager_topic(self, msg: mqtt.MQTTMessage):
""" Handler für die LoadMananger-Topics
Comment thread packages/control/loadmanagement.py Outdated
Comment on lines +192 to +198
if dimming_power_left is not None:
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, limit
Comment on lines +188 to +198
def _limit_loadmanager(self,
available_currents: List[float],
cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]:
dimming_power_left, limit = data.data.io_actions.dimming_load_manager({"type": "cp", "id": cp.num})
if dimming_power_left is not None:
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, limit
Comment thread packages/control/loadmanagement.py
Comment on lines +25 to +32
fixed_import_power = 0
for device in self.config.configuration.devices:
if device["type"] != "cp":
fixed_import_power += 4200
log.debug(f"Dimmen per LoadManager: Fest vergebene Mindestleistung: {fixed_import_power}W")
if fixed_import_power != self.config.configuration.fixed_import_power:
self.config.configuration.fixed_import_power = fixed_import_power
Pub().pub(f"openWB/set/io/action/{self.config.id}/config", asdict(self.config))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fixed_import_power gibt es beim Lastmanager nicht


def setup(self) -> None:
if check_fault_state_io_device(self.config.configuration.io_device):
max_power = self.config.configuration.fixed_import_power

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fixed_import_power gibt es beim Lastmanager nicht, sondern "maximale Leistung im Fehlerfall"

@AlexanderHa98 AlexanderHa98 added this to the 2.3.0 milestone Jul 9, 2026

@LKuemmel LKuemmel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Es kommen auch max_currents rein, die müssen auch berücksichtigt werden ;)
Und bitte noch eine Prüfung des Timestamps im IO-Device einbauen. Wenn der älter als 60s ist, eine Exception werfen.

self.config.configuration.fixed_import_power = fixed_import_power
Pub().pub(f"openWB/set/io/action/{self.config.id}/config", asdict(self.config))

control_command_log.info("Begrenzung per Lastmanager.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Einträge im control_command_log braucht man nicht. Bei §14a muss für den Netzbetreiber dokumentiert werden, das und wie viel gedimmt wurde, dafür ist dieses Log da.

Comment thread packages/control/loadmanagement.py Outdated
"type": "cp", "id": cp.num})

if loadmanager_power_left is not None and loadmanager_current_left is not None:
if sum(available_currents)*230 > loadmanager_power_left:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Es gab irgendwann mal den Wunsch von der Community, dass das Lastmanagement genauer rechnen soll. Also mit der tatsächlichen Spannung voltages_mean(cp.data.get.voltages) statt 230V

Comment on lines +35 to +36
if timestamp > 1e10:
timestamp /= 1000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if timestamp > 1e10:
timestamp /= 1000

Der Timestamp sollte als Unix-Timestamp kommen.

age_s = timecheck.create_timestamp() - timestamp
# < = deaktiviert
if age_s < 60:
log.warning("Lastmanager-Daten veraltet: age=%.1fs, timestamp=%s", age_s, timestamp)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
log.warning("Lastmanager-Daten veraltet: age=%.1fs, timestamp=%s", age_s, timestamp)

Die Exception sollte ohnehin geloggt werden. Dann kann die Warnung weg, sonst ist es doppelt.

io_state.digital_input = getattr(io_state, "digital_input", None) or {}
io_state.digital_output = getattr(io_state, "digital_output", None) or {}

if received_topics.get("openWB/mqtt/loadmanager/set/loadmanager"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Für die eindeutige Zuordnung wäre es gut, wenn noch die ID vom IO-Gerät im Topic ist. Dann ist es auch kein Problem, wenn mehrere Lastmanager konfiguriert werden -> openWB/mqtt/loadmanager/1/set/loadmanager
Das müsste dann auch noch in der setdata angepasst werden.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants