Skip to content
Merged
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
7 changes: 4 additions & 3 deletions docs/samples/sample_modbus/sample_modbus/bat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
from enum import IntEnum
from typing import Optional, TypedDict, Any
from typing import TypedDict, Any
from modules.common.abstract_device import AbstractBat
from modules.common.component_state import BatState
from modules.common.component_type import ComponentDescriptor
Expand All @@ -11,6 +11,7 @@
from modules.devices.sample_modbus.sample_modbus.config import SampleBatSetup
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint


class KwargsDict(TypedDict):
Expand Down Expand Up @@ -79,11 +80,11 @@ def update(self) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
Comment thread
ndrsnhs marked this conversation as resolved.
Resolved
# Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive
# Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird
# None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen.
self.client.write_register(reg, power_limit)
self.client.write_register(reg, setpoint.power_limit)
# Wenn der Speicher keine Steuerung der Ladeleistung unterstützt
pass

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
from typing import Optional, TypedDict, Any
from typing import TypedDict, Any
from modules.common import req
from modules.common.abstract_device import AbstractBat
from modules.common.component_state import BatState
Expand All @@ -10,6 +10,7 @@
from modules.devices.sample_request_by_component.sample_request_by_component.config import SampleBatSetup
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint


class KwargsDict(TypedDict):
Expand Down Expand Up @@ -45,11 +46,11 @@ def update(self) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
# Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive
# Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird
# None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen.
self.client.write_register(reg, power_limit)
self.client.write_register(reg, setpoint.power_limit)
# Wenn der Speicher keine Steuerung der Ladeleistung unterstützt
pass

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from modules.devices.sample_request_by_device.sample_request_by_device.config import SampleBatSetup
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint


class KwargsDict(TypedDict):
Expand Down Expand Up @@ -42,11 +43,11 @@ def update(self, response) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
Comment thread
ndrsnhs marked this conversation as resolved.
Resolved
# Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive
# Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird
# None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen.
self.client.write_register(reg, power_limit)
self.client.write_register(reg, setpoint.power_limit)
# Wenn der Speicher keine Steuerung der Ladeleistung unterstützt
pass

Expand Down
1 change: 1 addition & 0 deletions packages/control/bat.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def get_factory() -> Get:
@dataclass
class Set:
power_limit: Optional[int] = field(default=None, metadata={"topic": "set/power_limit"})
evu_power: Optional[int] = field(default=None, metadata={"topic": "set/evu_power"})


def set_factory() -> Set:
Expand Down
3 changes: 2 additions & 1 deletion packages/control/bat_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ def _set_bat_power_active_control(self, power):
log.debug(("Aktive Speichersteuerung: Ladung - "
f"Speicher (ID: {bat_component.component_config.id}) "
f"laden mit {power_limit} ({factor} x {bat_component_data.get.max_charge_power}) W"))
data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit = power_limit
bat_component_data.set.power_limit = power_limit
bat_component_data.set.evu_power = data.data.counter_all_data.get_evu_counter().data.get.power

def setup_bat(self):
""" prüft, ob mind ein Speicher vorhanden ist und berechnet die Summen-Topics.
Expand Down
6 changes: 5 additions & 1 deletion packages/control/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from modules.io_actions.controllable_consumers.dimming_direct_control.api import DimmingDirectControl
from modules.io_actions.generator_systems.stepwise_control.api_eebus import StepwiseControlEebus
from modules.io_actions.generator_systems.stepwise_control.api_io import StepwiseControlIo
from dataclasses import replace

log = logging.getLogger(__name__)
control_command_log = logging.getLogger("steuve_control_command")
Expand Down Expand Up @@ -71,11 +72,14 @@ def process_algorithm_results(self) -> None:
log.exception("Fehler im Process-Modul für Ladepunkt "+str(cp))
if data.data.bat_all_data.data.set.set_limit:
for bat_component in get_bat_components_by_controllability()[0]:
setpoint = replace(
data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set
)
modules_threads.append(
Thread(
target=set_power_limit_wrapper,
args=(bat_component,
data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit),
setpoint),
name=f"set power limit {bat_component.component_config.id}"))
for consumer in data.data.consumer_data.values():
try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def _get_data_path() -> Path:
INTERNAL_TOPICS = ['openWB/bat/set/charging_power_left',
'openWB/bat/set/regulate_up',
'openWB/bat/+/set/power_limit',
'openWB/bat/+/set/evu_power',
'openWB/command/max_id/autolock_plan',
'openWB/command/max_id/charge_template',
'openWB/command/max_id/charge_template_scheduled_plan',
Expand Down
2 changes: 2 additions & 0 deletions packages/helpermodules/setdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,8 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage):
self._validate_value(msg, bool)
elif "/set/power_limit" in msg.topic:
self._validate_value(msg, float)
elif "/set/evu_power" in msg.topic:
self._validate_value(msg, float)
else:
self.__unknown_topic(msg)
else:
Expand Down
1 change: 1 addition & 0 deletions packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class UpdateConfig:
"^openWB/bat/[0-9]+/get/fault_str$",
"^openWB/bat/[0-9]+/get/power_limit_controllable$",
"^openWB/bat/[0-9]+/set/power_limit$",
"^openWB/bat/[0-9]+/set/evu_power$",

"^openWB/chargepoint/get/power$",
"^openWB/chargepoint/get/exported$",
Expand Down
10 changes: 8 additions & 2 deletions packages/modules/common/abstract_device.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from __future__ import annotations
from abc import abstractmethod
from typing import Optional, Type
from typing import Optional, Type, Protocol


class SetPoint(Protocol):
power_limit: Optional[int]
evu_power: Optional[int]


class AbstractDevice:
Expand Down Expand Up @@ -31,7 +37,7 @@ def update(self, *kwargs) -> None:
pass

@abstractmethod
def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
Comment thread
ndrsnhs marked this conversation as resolved.
Resolved
# power limit None heißt, auf maximale Speicherleistung setzen = Speicher-Begrenzung aufheben
pass

Expand Down
7 changes: 4 additions & 3 deletions packages/modules/common/configurable_device.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import inspect
import logging
from typing import Optional, TypeVar, Generic, Dict, Any, Callable, Iterable, List
from typing import TypeVar, Generic, Dict, Any, Callable, Iterable, List

from dataclass_utils import dataclass_from_dict
from helpermodules import timecheck
from helpermodules.pub import Pub
from modules.common.abstract_device import AbstractBat, AbstractDevice
from modules.common.component_context import SingleComponentUpdateContext, MultiComponentUpdateContext
from modules.common.fault_state import ComponentInfo, FaultState
from control.bat import Set as SetPoint

T_DEVICE_CONFIG = TypeVar("T_DEVICE_CONFIG")
T_COMPONENT = TypeVar("T_COMPONENT")
Expand Down Expand Up @@ -130,9 +131,9 @@ def update(self):
self.__component_updater(initialized_components, self.error_handler)


def set_power_limit_wrapper(bat_component: AbstractBat, power_limit: Optional[int]):
def set_power_limit_wrapper(bat_component: AbstractBat, setpoint: SetPoint):
"""set_power_limit innerhalb des SingleComponentUpdateContext aufrufen,
damit Fehler im fault_state-Handler behandelt werden
"""
with SingleComponentUpdateContext(bat_component.fault_state, update_always=False):
bat_component.set_power_limit(power_limit)
bat_component.set_power_limit(setpoint)
9 changes: 5 additions & 4 deletions packages/modules/devices/alpha_ess/alpha_ess/bat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import time
from typing import TypedDict, Any, Optional
from typing import TypedDict, Any
from modules.common.abstract_device import AbstractBat
from modules.common.component_state import BatState
from modules.common.component_type import ComponentDescriptor
Expand All @@ -11,6 +11,7 @@
from modules.devices.alpha_ess.alpha_ess.config import AlphaEssBatSetup
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -62,16 +63,16 @@ def update(self) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
unit = self.__modbus_id

if power_limit is None:
if setpoint.power_limit is None:
# Kein Powerlimit gefordert, externe Steuerung deaktivieren
log.debug("Keine Batteriesteuerung gefordert, deaktiviere externe Steuerung.")
if self.last_mode is not None:
self.__tcp_client.write_register(2127, 0, data_type=ModbusDataType.UINT_16, unit=unit)
self.last_mode = None
elif power_limit <= 0:
elif setpoint.power_limit <= 0:
# AlphaESS kann die Entladung nur über den SoC verhindern (komplette Entladesperre)
# Netzladung mit geringen Ziel SoC verhindert auch Entladung (Default 10%)
# Zeiten für Netzladung müssen im Wechselrichter aktiviert werden
Expand Down
11 changes: 6 additions & 5 deletions packages/modules/devices/anker/solarbank/bat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
import logging
from typing import Any, Optional, TypedDict
from typing import Any, TypedDict

from modules.common.abstract_device import AbstractBat
from modules.common.component_state import BatState
Expand All @@ -12,6 +12,7 @@
from modules.devices.anker.solarbank.config import Anker, AnkerBatSetup
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -52,10 +53,10 @@ def update(self) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
unit = self.device_config.configuration.modbus_id

if power_limit is None:
if setpoint.power_limit is None:
log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter")
if self.last_mode is not None:
self.client.write_register(10064, 0, data_type=ModbusDataType.UINT_16, unit=unit)
Expand All @@ -65,9 +66,9 @@ def set_power_limit(self, power_limit: Optional[int]) -> None:
self.client.write_register(10064, 3, data_type=ModbusDataType.UINT_16, unit=unit)
self.last_mode = 'limited'

power_value = 0 if power_limit == 0 else int(power_limit) * -1
power_value = 0 if setpoint.power_limit == 0 else int(setpoint.power_limit) * -1
self.client.write_register(10071, power_value, data_type=ModbusDataType.INT_32, unit=unit)
log.debug("Aktive Batteriesteuerung angefordert, angeforderte Leistung: {power_value} W")
log.debug(f"Aktive Batteriesteuerung angefordert, angeforderte Leistung: {power_value} W")

def power_limit_controllable(self) -> bool:
return True
Expand Down
9 changes: 5 additions & 4 deletions packages/modules/devices/batterx/batterx/bat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
import logging
from typing import Dict, TypedDict, Any, Optional
from typing import Dict, TypedDict, Any

from modules.devices.batterx.batterx.config import BatterXBatSetup
from modules.common.abstract_device import AbstractBat
Expand All @@ -12,6 +12,7 @@
from modules.common import req
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -49,10 +50,10 @@ def update(self, resp: Dict) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
log.debug(f'last_mode: {self.last_mode}')

if power_limit is None:
if setpoint.power_limit is None:
# Kein Powerlimit gefordert, externe Steuerung deaktivieren
log.debug("Keine Batteriesteuerung gefordert, deaktiviere externe Steuerung.")
if self.last_mode is not None:
Expand All @@ -67,7 +68,7 @@ def set_power_limit(self, power_limit: Optional[int]) -> None:
timeout=5
)
self.last_mode = None
elif power_limit <= 0:
elif setpoint.power_limit <= 0:
# BatterX kann Entladung nur komplett sperren
log.debug("Aktive Batteriesteuerung angestoßen. Setze Entladesperre.")
if self.last_mode != 'stop':
Expand Down
21 changes: 11 additions & 10 deletions packages/modules/devices/fox_ess/fox_ess_h3_smart/bat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
import logging
from typing import TypedDict, Any, Optional
from typing import TypedDict, Any

from modules.common.abstract_device import AbstractBat
from modules.common.component_state import BatState
Expand All @@ -12,6 +12,7 @@
from modules.devices.fox_ess.fox_ess_h3_smart.config import FoxEssH3SmartBatSetup
from modules.common.utils.peak_filter import PeakFilter
from modules.common.component_type import ComponentType
from control.bat import Set as SetPoint

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -50,17 +51,17 @@ def update(self) -> None:
)
self.store.set(bat_state)

def set_power_limit(self, power_limit: Optional[int]) -> None:
def set_power_limit(self, setpoint: SetPoint) -> None:
unit = self.component_config.configuration.modbus_id

if power_limit is None:
if setpoint.power_limit is None:
log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter")
if self.last_mode is not None:
# remote control disabled, mode self use (1)
self.__tcp_client.write_register(46001, 0, data_type=ModbusDataType.UINT_16, unit=unit)
self.__tcp_client.write_register(49203, 1, data_type=ModbusDataType.UINT_16, unit=unit)
self.last_mode = None
elif power_limit == 0:
elif setpoint.power_limit == 0:
# remote control enabled, watchdog 60s, active power 0W, mode FeedIn Priority (2)
log.debug("Aktive Batteriesteuerung. FoxEss H3 Smart wird auf Stop gesetzt und nicht entladen")
if self.last_mode != 'stop':
Expand All @@ -69,26 +70,26 @@ def set_power_limit(self, power_limit: Optional[int]) -> None:
self.__tcp_client.write_register(49203, 2, data_type=ModbusDataType.UINT_16, unit=unit)
self.last_mode = 'stop'
self.__tcp_client.write_register(46003, 0, data_type=ModbusDataType.UINT_16, unit=unit)
elif power_limit < 0:
elif setpoint.power_limit < 0:
# remote control enabled, watchdog 60s, active power 0W, mode Backup (3)
log.debug(f"Aktive Batteriesteuerung FoxEss H3 Smart:"
f"Speicher soll mit {power_limit} W entladen werden")
f"Speicher soll mit {setpoint.power_limit} W entladen werden")
if self.last_mode != 'discharge':
self.__tcp_client.write_register(46001, 1, data_type=ModbusDataType.UINT_16, unit=unit)
self.__tcp_client.write_register(46002, 60, data_type=ModbusDataType.UINT_16, unit=unit)
self.__tcp_client.write_register(49203, 3, data_type=ModbusDataType.UINT_16, unit=unit)
self.last_mode = 'discharge'
self.__tcp_client.write_register(46003, -power_limit, data_type=ModbusDataType.UINT_16, unit=unit)
elif power_limit > 0:
self.__tcp_client.write_register(46003, -setpoint.power_limit, data_type=ModbusDataType.UINT_16, unit=unit)
elif setpoint.power_limit > 0:
# remote control enabled, watchdog 60s, active power 0W, mode Backup (3)
log.debug(f"Aktive Batteriesteuerung FoxEss H3 Smart:"
f"Speicher soll mit {power_limit} W geladen werden")
f"Speicher soll mit {setpoint.power_limit} W geladen werden")
if self.last_mode != 'charge':
self.__tcp_client.write_register(46001, 1, data_type=ModbusDataType.UINT_16, unit=unit)
self.__tcp_client.write_register(46002, 60, data_type=ModbusDataType.UINT_16, unit=unit)
self.__tcp_client.write_register(49203, 3, data_type=ModbusDataType.UINT_16, unit=unit)
self.last_mode = 'charge'
self.__tcp_client.write_register(46003, -power_limit, data_type=ModbusDataType.UINT_16, unit=unit)
self.__tcp_client.write_register(46003, -setpoint.power_limit, data_type=ModbusDataType.UINT_16, unit=unit)

def power_limit_controllable(self) -> bool:
return True
Expand Down
Loading
Loading