diff --git a/data/config/mosquitto/openwb_local.conf b/data/config/mosquitto/openwb_local.conf index 4999880fd3..13fbd9ec8c 100644 --- a/data/config/mosquitto/openwb_local.conf +++ b/data/config/mosquitto/openwb_local.conf @@ -1,4 +1,4 @@ -# openwb-version:15 +# openwb-version:16 listener 1886 localhost allow_anonymous true @@ -33,10 +33,11 @@ topic openWB/pv/get/# out 2 topic openWB/pv/+/config/# out 2 topic openWB/pv/+/get/# out 2 -topic openWB/bat/config/configured out 2 +topic openWB/bat/config/# out 2 topic openWB/bat/get/# out 2 topic openWB/bat/+/config/# out 2 topic openWB/bat/+/get/# out 2 +topic openWB/bat/+/set/# out 2 topic openWB/general/# out 2 diff --git a/docs/Neues Modul programmieren.md b/docs/Neues Modul programmieren.md index 476ae462c2..99bb915d58 100644 --- a/docs/Neues Modul programmieren.md +++ b/docs/Neues Modul programmieren.md @@ -33,6 +33,8 @@ Wenn von der Komponente die Zählerstände für Import und Export gelesen werden Bei Hybrid-Systemen erfolgt die Verrechnung von Speicher-und PV-Leistung automatisiert, wenn Speicher und Wechselrichter in der Hierarchie wie [hier](https://github.com/openWB/core/wiki/Hybrid-System-aus-Wechselrichter-und-Speicher) beschrieben angeordnet sind. Wenn noch weitere spezifische Berechnungen erforderlich sind, müsst Ihr die Komponenten wie unter sample_request_per_device abfragen. Die update-Methode der Komponenten wird dann in eine get- und set-Methode aufgeteilt. Die get-Methode liefert den Component-State zurück, dieser wird in der update_components-Methode des Geräts verrechnet und dann die set-Methode der Komponente aufgerufen, die die store-Methode der Komponente aufruft. +Bei Speichern, die eine aktive Steuerung unterstützen, kann mit der Methode `set_power_limit` die Speicherleistung gesetzt werden. Die Speicher erben von der Klasse `AbstractBat`, die die abstrakte Methode `set_power_limit` beinhaltet. Bei der Implementierung des Speichers kannst Du diese Methode überschreiben. Die Regelung prüft am Ende, ob die Methode für den jeweiligen Speicher implementiert ist und ruft diese auf. Als Variable wird die Speicherleistung in Watt oder `None` übergeben, dann wird der Speicher nicht mehr aktiv von der openWB gesteuert und soll selbst anhand des EVU-Punktes regeln. + ### Neues Fahrzeug programmieren Beim Aufruf der _updater_-Funktion wird die Variable _vehicle_update_data_ übergeben. Darin sind aktuelle Daten aus der Regelung, wie zB Stecker-Status oder die geladene Energie seit Anstecken, enthalten, um Besonderheiten wie zB das Aufwecken des Fahrzeugs oder eine manuelle Berechnung während des Ladevorgangs umsetzen zu können. diff --git a/packages/conftest.py b/packages/conftest.py index 751edcec83..a66e5bbc15 100644 --- a/packages/conftest.py +++ b/packages/conftest.py @@ -5,6 +5,7 @@ from control import data from control.bat import Bat, BatData from control.bat import Get as BatGet +from control.bat import Set as BatSet from control.chargepoint.chargepoint import Chargepoint, ChargepointData from control.chargepoint.chargepoint_data import Config, Get, Set from control.counter import Counter, CounterData @@ -130,9 +131,10 @@ def data_() -> None: daily_imported=10000, daily_exported=0, imported=62000, fault_state=0), set=Mock(spec=Set, loadmanagement_available=True)))} - data.data.bat_data.update({"bat2": Mock(spec=Bat, data=Mock(spec=BatData, get=Mock( + data.data.bat_data.update({"bat2": Mock(spec=Bat, num=2, data=Mock(spec=BatData, get=Mock( spec=BatGet, power=-5000, daily_imported=7000, daily_exported=3000, imported=12000, exported=10000, - currents=None, fault_state=0)))}) + currents=None, fault_state=0), + set=Mock(spec=BatSet, power_limit=None)))}) data.data.pv_data.update({"pv1": Mock(spec=Pv, data=Mock( spec=PvData, get=Mock(spec=PvGet, power=-10000, daily_exported=6000, exported=27000, currents=None, fault_state=0)))}) diff --git a/packages/control/algorithm/additional_current.py b/packages/control/algorithm/additional_current.py index a57daa48a0..ad92724e14 100644 --- a/packages/control/algorithm/additional_current.py +++ b/packages/control/algorithm/additional_current.py @@ -1,6 +1,7 @@ import logging from control.algorithm import common +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT from control.loadmanagement import LimitingValue, Loadmanagement from control.counter import Counter from control.chargepoint.chargepoint import Chargepoint @@ -12,14 +13,13 @@ class AdditionalCurrent: - CONSIDERED_CHARGE_MODES = common.CHARGEMODES[0:8] def __init__(self) -> None: pass def set_additional_current(self) -> None: - common.reset_current_by_chargemode(self.CONSIDERED_CHARGE_MODES) - for mode_tuple, counter in common.mode_and_counter_generator(self.CONSIDERED_CHARGE_MODES): + common.reset_current_by_chargemode(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT) + for mode_tuple, counter in common.mode_and_counter_generator(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT): preferenced_chargepoints, preferenced_cps_without_set_current = get_preferenced_chargepoint_charging( get_chargepoints_by_mode_and_counter(mode_tuple, f"counter{counter.num}")) if preferenced_chargepoints: diff --git a/packages/control/algorithm/chargemodes.py b/packages/control/algorithm/chargemodes.py new file mode 100644 index 0000000000..65e16a9645 --- /dev/null +++ b/packages/control/algorithm/chargemodes.py @@ -0,0 +1,26 @@ +from control.chargemode import Chargemode + +# Lademodi in absteigender Priorität +# Tupel-Inhalt:(eingestellter Modus, tatsächlich genutzter Modus, Priorität) +CHARGEMODES = ((Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING, True), + (Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING, False), + (None, Chargemode.TIME_CHARGING, True), + (None, Chargemode.TIME_CHARGING, False), + (Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING, True), + (Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING, False), + (Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING, True), + (Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING, False), + (Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING, True), + (Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING, False), + (Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, True), + (Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, False), + (None, Chargemode.STANDBY, True), + (None, Chargemode.STANDBY, False), + (None, Chargemode.STOP, True), + (None, Chargemode.STOP, False)) + +CONSIDERED_CHARGE_MODES_SURPLUS = CHARGEMODES[0:2] + CHARGEMODES[6:12] +CONSIDERED_CHARGE_MODES_PV_ONLY = CHARGEMODES[8:12] +CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT = CHARGEMODES[0:8] +CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-1] +CONSIDERED_CHARGE_MODES_NO_CURRENT = CHARGEMODES[12:16] diff --git a/packages/control/algorithm/common.py b/packages/control/algorithm/common.py index d4f72bd251..c2f8d4eaf7 100644 --- a/packages/control/algorithm/common.py +++ b/packages/control/algorithm/common.py @@ -3,7 +3,6 @@ from control import data from control.algorithm.filter_chargepoints import get_chargepoints_by_mode -from control.chargemode import Chargemode from control.chargepoint.chargepoint import Chargepoint from control.counter import Counter from helpermodules.timecheck import check_timestamp @@ -11,24 +10,6 @@ log = logging.getLogger(__name__) -# Lademodi in absteigender Priorität -# Tupel-Inhalt:(eingestellter Modus, tatsächlich genutzter Modus, Priorität) -CHARGEMODES = ((Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING, True), - (Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING, False), - (None, Chargemode.TIME_CHARGING, True), - (None, Chargemode.TIME_CHARGING, False), - (Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING, True), - (Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING, False), - (Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING, True), - (Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING, False), - (Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING, True), - (Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING, False), - (Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, True), - (Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, False), - (None, Chargemode.STANDBY, True), - (None, Chargemode.STANDBY, False), - (None, Chargemode.STOP, True), - (None, Chargemode.STOP, False)) LESS_CHARGING_TIMEOUT = 60 diff --git a/packages/control/algorithm/filter_chargepoints.py b/packages/control/algorithm/filter_chargepoints.py index c87a86fa62..5b519ad42d 100644 --- a/packages/control/algorithm/filter_chargepoints.py +++ b/packages/control/algorithm/filter_chargepoints.py @@ -104,3 +104,10 @@ def _get_preferenced_chargepoint(valid_chargepoints: List[Chargepoint]) -> List: except Exception: log.exception("Fehler im Algorithmus-Modul") return preferenced_chargepoints + + +def get_chargepoints_by_chargemodes(modes) -> List[Chargepoint]: + chargepoints: List[Chargepoint] = [] + for mode in modes: + chargepoints.extend(get_chargepoints_by_mode(mode)) + return chargepoints diff --git a/packages/control/algorithm/min_current.py b/packages/control/algorithm/min_current.py index 70d2d20240..87d7d9a134 100644 --- a/packages/control/algorithm/min_current.py +++ b/packages/control/algorithm/min_current.py @@ -1,6 +1,7 @@ import logging from control.algorithm import common +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_MIN_CURRENT from control.loadmanagement import Loadmanagement from control.algorithm.filter_chargepoints import get_chargepoints_by_mode_and_counter from modules.common.utils.component_parser import get_component_name_by_id @@ -9,13 +10,12 @@ class MinCurrent: - CONSIDERED_CHARGE_MODES = common.CHARGEMODES[0:-1] def __init__(self) -> None: pass def set_min_current(self) -> None: - for mode_tuple, counter in common.mode_and_counter_generator(self.CONSIDERED_CHARGE_MODES): + for mode_tuple, counter in common.mode_and_counter_generator(CONSIDERED_CHARGE_MODES_MIN_CURRENT): preferenced_chargepoints = get_chargepoints_by_mode_and_counter(mode_tuple, f"counter{counter.num}") if preferenced_chargepoints: log.info(f"Mode-Tuple {mode_tuple[0]} - {mode_tuple[1]} - {mode_tuple[2]}, Zähler {counter.num}") diff --git a/packages/control/algorithm/no_current.py b/packages/control/algorithm/no_current.py index 3c17a27aba..0b55fed852 100644 --- a/packages/control/algorithm/no_current.py +++ b/packages/control/algorithm/no_current.py @@ -1,10 +1,8 @@ import logging -from typing import List from control import data -from control.algorithm.common import CHARGEMODES -from control.chargepoint.chargepoint import Chargepoint -from control.algorithm.filter_chargepoints import get_chargepoints_by_mode +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_NO_CURRENT +from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes log = logging.getLogger(__name__) @@ -14,9 +12,7 @@ def __init__(self) -> None: pass def set_no_current(self) -> None: - chargepoints: List[Chargepoint] = [] - for mode in CHARGEMODES[12:16]: - chargepoints.extend(get_chargepoints_by_mode(mode)) + chargepoints = get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_NO_CURRENT) for cp in chargepoints: cp.data.set.current = 0 diff --git a/packages/control/algorithm/surplus_controlled.py b/packages/control/algorithm/surplus_controlled.py index 52612a22d3..ece81efac2 100644 --- a/packages/control/algorithm/surplus_controlled.py +++ b/packages/control/algorithm/surplus_controlled.py @@ -3,19 +3,19 @@ from control import data from control.algorithm import common +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_PV_ONLY, CONSIDERED_CHARGE_MODES_SURPLUS +from control.algorithm.filter_chargepoints import (get_chargepoints_by_chargemodes, + get_chargepoints_by_mode_and_counter, + get_preferenced_chargepoint_charging) from control.chargepoint.charging_type import ChargingType -from control.loadmanagement import LimitingValue, Loadmanagement -from control.counter import ControlRangeState, Counter from control.chargepoint.chargepoint import Chargepoint -from control.algorithm.filter_chargepoints import (get_chargepoints_by_mode, get_chargepoints_by_mode_and_counter, - get_preferenced_chargepoint_charging) from control.chargepoint.chargepoint_state import ChargepointState, CHARGING_STATES from modules.common.utils.component_parser import get_component_name_by_id +from control.counter import ControlRangeState, Counter +from control.loadmanagement import LimitingValue, Loadmanagement -log = logging.getLogger(__name__) -CONSIDERED_CHARGE_MODES = common.CHARGEMODES[0:2] + common.CHARGEMODES[6:12] -CONSIDERED_CHARGE_MODES_PV = common.CHARGEMODES[8:12] +log = logging.getLogger(__name__) class SurplusControlled: @@ -24,8 +24,8 @@ def __init__(self) -> None: pass def set_surplus_current(self) -> None: - common.reset_current_by_chargemode(CONSIDERED_CHARGE_MODES) - for mode_tuple, counter in common.mode_and_counter_generator(CONSIDERED_CHARGE_MODES): + common.reset_current_by_chargemode(CONSIDERED_CHARGE_MODES_SURPLUS) + for mode_tuple, counter in common.mode_and_counter_generator(CONSIDERED_CHARGE_MODES_SURPLUS): preferenced_chargepoints, preferenced_cps_without_set_current = get_preferenced_chargepoint_charging( get_chargepoints_by_mode_and_counter(mode_tuple, f"counter{counter.num}")) cp_with_feed_in, cp_without_feed_in = self.filter_by_feed_in_limit(preferenced_chargepoints) @@ -148,7 +148,7 @@ def _fix_deviating_evse_current(self, limited_current, chargepoint: Chargepoint) def check_submode_pv_charging(self) -> None: evu_counter = data.data.counter_all_data.get_evu_counter() - for cp in get_chargepoints_pv_charging(): + for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_PV_ONLY): def phase_switch_necessary() -> bool: return cp.cp_ev_chargemode_support_phase_switch() and cp.data.get.phases_in_use != 1 control_parameter = cp.data.control_parameter @@ -176,13 +176,13 @@ def phase_switch_necessary() -> bool: control_parameter.required_currents = [0]*3 def check_switch_on(self) -> None: - for cp in get_chargepoints_pv_charging(): + for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_SURPLUS): if (cp.data.control_parameter.state == ChargepointState.NO_CHARGING_ALLOWED or cp.data.control_parameter.state == ChargepointState.SWITCH_ON_DELAY): data.data.counter_all_data.get_evu_counter().switch_on_threshold_reached(cp) def set_required_current_to_max(self) -> None: - for cp in get_chargepoints_surplus_controlled(): + for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_PV_ONLY): charging_ev_data = cp.data.set.charging_ev_data required_currents = cp.data.control_parameter.required_currents control_parameter = cp.data.control_parameter @@ -202,17 +202,3 @@ def set_required_current_to_max(self) -> None: control_parameter.required_currents = [max_current if required_currents[i] != 0 else 0 for i in range(3)] control_parameter.required_current = max_current - - -def get_chargepoints_pv_charging() -> List[Chargepoint]: - chargepoints: List[Chargepoint] = [] - for mode in CONSIDERED_CHARGE_MODES_PV: - chargepoints.extend(get_chargepoints_by_mode(mode)) - return chargepoints - - -def get_chargepoints_surplus_controlled() -> List[Chargepoint]: - chargepoints: List[Chargepoint] = [] - for mode in CONSIDERED_CHARGE_MODES: - chargepoints.extend(get_chargepoints_by_mode(mode)) - return chargepoints diff --git a/packages/control/algorithm/surplus_controlled_test.py b/packages/control/algorithm/surplus_controlled_test.py index cc794ee605..bd275969bc 100644 --- a/packages/control/algorithm/surplus_controlled_test.py +++ b/packages/control/algorithm/surplus_controlled_test.py @@ -4,7 +4,8 @@ from control import data from control.algorithm import surplus_controlled -from control.algorithm.surplus_controlled import SurplusControlled, get_chargepoints_pv_charging +from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes +from control.algorithm.surplus_controlled import CONSIDERED_CHARGE_MODES_PV_ONLY, SurplusControlled from control.chargemode import Chargemode from control.chargepoint.chargepoint import Chargepoint, ChargepointData from control.chargepoint.chargepoint_data import Get, Set @@ -89,7 +90,7 @@ def test_set_required_current_to_max(phases: int, required_currents=required_currents)) mock_cp1.template = CpTemplate() mock_get_chargepoints_surplus_controlled = Mock(return_value=[mock_cp1]) - monkeypatch.setattr(surplus_controlled, "get_chargepoints_surplus_controlled", + monkeypatch.setattr(surplus_controlled, "get_chargepoints_by_chargemodes", mock_get_chargepoints_surplus_controlled) # execution @@ -149,7 +150,7 @@ def setup_cp(cp: Chargepoint, submode: str) -> Chargepoint: "cp2": setup_cp(mock_cp2, submode_2)} # evaluation - chargepoints = get_chargepoints_pv_charging() + chargepoints = get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_PV_ONLY) # assertion assert chargepoints == expected_chargepoints diff --git a/packages/control/bat.py b/packages/control/bat.py index 0458e3e588..be2d8547c4 100644 --- a/packages/control/bat.py +++ b/packages/control/bat.py @@ -20,15 +20,26 @@ class Get: fault_state: int = field(default=0, metadata={"topic": "get/fault_state"}) fault_str: str = field(default=NO_ERROR, metadata={"topic": "get/fault_str"}) power: float = field(default=0, metadata={"topic": "get/power"}) + power_limit_controlable: bool = field(default=False, metadata={"topic": "get/power_limit_controlable"}) def get_factory() -> Get: return Get() +@dataclass +class Set: + power_limit: float = field(default=0, metadata={"topic": "set/power_limit"}) + + +def set_factory() -> Set: + return Set() + + @dataclass class BatData: get: Get = field(default_factory=get_factory) + set: Set = field(default_factory=set_factory) class Bat: diff --git a/packages/control/bat_all.py b/packages/control/bat_all.py index b4aa40021b..68715c0fc9 100644 --- a/packages/control/bat_all.py +++ b/packages/control/bat_all.py @@ -20,10 +20,14 @@ from dataclasses import dataclass, field from enum import Enum import logging +from typing import List from control import data +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT +from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes from control.bat import Bat from helpermodules.constants import NO_ERROR +from modules.common.abstract_device import AbstractDevice from modules.common.fault_state import FaultStateLevel log = logging.getLogger(__name__) @@ -35,9 +39,17 @@ class BatConsiderationMode(Enum): MIN_SOC_BAT = "min_soc_bat_mode" +class BatPowerLimitMode(Enum): + NO_LIMIT = "no_limit" + LIMIT_STOP = "limit_stop" + LIMIT_TO_HOME_CONSUMPTION = "limit_to_home_consumption" + + @dataclass class Config: configured: bool = field(default=False, metadata={"topic": "config/configured"}) + power_limit_mode: str = field(default=BatPowerLimitMode.NO_LIMIT.value, + metadata={"topic": "config/power_limit_mode"}) def config_factory() -> Config: @@ -46,6 +58,7 @@ def config_factory() -> Config: @dataclass class Get: + power_limit_controlable: bool = field(default=False, metadata={"topic": "get/power_limit_controlable"}) soc: float = field(default=0, metadata={"topic": "get/soc"}) daily_exported: float = field(default=0, metadata={"topic": "get/daily_exported"}) daily_imported: float = field(default=0, metadata={"topic": "get/daily_imported"}) @@ -64,6 +77,7 @@ def get_factory() -> Get: class Set: charging_power_left: float = field( default=0, metadata={"topic": "set/charging_power_left"}) + power_limit: float = field(default=0, metadata={"topic": "set/power_limit"}) regulate_up: bool = field(default=False, metadata={"topic": "set/regulate_up"}) @@ -180,6 +194,8 @@ def setup_bat(self): try: if self.data.config.configured is True: if self.data.get.fault_state == 0: + self.set_power_limit_controlable() + self.get_power_limit() self._get_charging_power_left() log.info(f"{self.data.set.charging_power_left}W verbleibende Speicher-Leistung") else: @@ -236,15 +252,19 @@ def _get_charging_power_left(self): # Speicher sollte weder ge- noch entladen werden, um den Mindest-SoC zu halten. charging_power_left = self.data.get.power else: - if config.bat_power_discharge_active: - # Wenn der Speicher mit mehr als der erlaubten Entladeleistung entladen wird, muss das - # vom Überschuss subtrahiert werden. - # Wenn der Speicher mit weniger als der erlaubten Entladeleistung entladen wird - charging_power_left = config.bat_power_discharge + self.data.get.power - log.debug(f"Erlaubte Entlade-Leistung nutzen {charging_power_left}W") + if self.data.set.power_limit is None: + if config.bat_power_discharge_active: + # Wenn der Speicher mit mehr als der erlaubten Entladeleistung entladen wird, muss das + # vom Überschuss subtrahiert werden. + charging_power_left = config.bat_power_discharge + self.data.get.power + log.debug(f"Erlaubte Entlade-Leistung nutzen {charging_power_left}W") + else: + # Speicher sollte weder ge- noch entladen werden. + charging_power_left = self.data.get.power else: - # Speicher sollte weder ge- noch entladen werden. - charging_power_left = self.data.get.power + log.debug("Keine erlaubte Entladeleistung freigeben, da der Speicher mit einer vorgegeben " + "Leistung entladen wird.") + charging_power_left = 0 # Keine Ladeleistung vom Speicher für Fahrzeuge einplanen, wenn max # Ausgangsleistung erreicht ist. if self.data.set.regulate_up: @@ -272,3 +292,60 @@ def power_for_bat_charging(self): except Exception: log.exception("Fehler im Bat-Modul") return 0 + + def set_power_limit_controlable(self): + controlable_bat_components = get_controlable_bat_components() + if len(controlable_bat_components) > 0: + self.data.get.power_limit_controlable = True + for bat in controlable_bat_components: + data.data.bat_data[f"bat{bat.component_config.id}"].data.get.power_limit_controlable = True + else: + self.data.get.power_limit_controlable = False + + def get_power_limit(self): + if (self.data.config.power_limit_mode != BatPowerLimitMode.NO_LIMIT.value + and len(get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT)) > 0 and + self.data.get.power_limit_controlable and + # Nur wenn kein Überschuss im System ist, Speicherleistung begrenzen. + self.data.get.power <= 0 and + data.data.counter_all_data.get_evu_counter().data.get.power >= 0): + if self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_STOP.value: + self.data.set.power_limit = 0 + elif self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_TO_HOME_CONSUMPTION.value: + self.data.set.power_limit = data.data.counter_all_data.data.set.home_consumption + log.debug(f"Speicher-Leistung begrenzen auf {self.data.set.power_limit/1000}kW") + else: + self.data.set.power_limit = None + if len(get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT)) == 0: + log.debug("Speicher-Leistung nicht begrenzen, " + "da keine Ladepunkte in einem Lademodus mit Netzbezug sind.") + elif self.data.get.power_limit_controlable is False: + log.debug("Speicher-Leistung nicht begrenzen, da keine regelbaren Speicher vorhanden sind.") + elif self.data.get.power > 0: + log.debug("Speicher-Leistung nicht begrenzen, da kein Speicher entladen wird.") + elif data.data.counter_all_data.get_evu_counter().data.get.power < 0: + log.debug("Speicher-Leistung nicht begrenzen, da EVU-Überschuss vorhanden ist.") + else: + log.debug("Speicher-Leistung nicht begrenzen.") + remaining_power_limit = self.data.set.power_limit + for bat_component in get_controlable_bat_components(): + if self.data.set.power_limit is None: + power_limit = None + else: + power_limit = min(self._max_bat_power_hybrid_system( + data.data.bat_data[f"bat{bat_component.component_config.id}"])[0], remaining_power_limit) + remaining_power_limit -= power_limit + remaining_power_limit = min(remaining_power_limit, 0) + + data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit = power_limit + + +def get_controlable_bat_components() -> List: + bat_components = [] + for value in data.data.system_data.values(): + if isinstance(value, AbstractDevice): + for comp_value in value.components.values(): + if "bat" in comp_value.component_config.type: + if "set_power_limit" in type(comp_value).__dict__: + bat_components.append(comp_value) + return bat_components diff --git a/packages/control/bat_all_test.py b/packages/control/bat_all_test.py index 5d23fe6532..08551f6d1f 100644 --- a/packages/control/bat_all_test.py +++ b/packages/control/bat_all_test.py @@ -1,13 +1,19 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from typing import List, Optional from unittest.mock import Mock import pytest +from packages.conftest import hierarchy_standard +from control import bat_all from control.bat import Bat -from control.bat_all import BatAll +from control.bat_all import BatAll, BatPowerLimitMode from control import data +from control.chargepoint.chargepoint import Chargepoint from control.chargepoint.chargepoint_all import AllChargepointData, AllChargepoints, AllGet from control.general import General, PvCharging from control.pv import Config, Get, Pv, PvData +from modules.devices.generic.mqtt.bat import MqttBat +from modules.devices.generic.mqtt.config import MqttBatSetup @pytest.fixture @@ -77,7 +83,7 @@ def test_limit_bat_power_discharge(required_power, return_max_bat_power_hybrid_s assert power == expected_power -@ dataclass +@dataclass class Params: name: str config: PvCharging @@ -85,6 +91,7 @@ class Params: soc: float expected_charging_power_left: float expected_regulate_up: bool + power_limit: Optional[float] = None cases = [ @@ -129,6 +136,10 @@ class Params: Params("Mindest-SoC, SoC erreicht, Entladung in Auto, Speicher voll", PvCharging(bat_mode="min_soc_bat_mode", bat_power_reserve=500, bat_power_reserve_active=True, min_bat_soc=100), 0, 100, 0, False), + Params(("Mindest-SoC, SoC erreicht, Entladung in Auto, Speicher lädt mit weniger als Entladeleistung, " + "Speicher-Sperre aktiv"), + PvCharging(bat_mode="min_soc_bat_mode", bat_power_discharge=500, bat_power_discharge_active=True), + 400, 90, 0, False, 600), ] @@ -138,6 +149,7 @@ def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypat b_all = BatAll() b_all.data.get.power = params.power b_all.data.get.soc = params.soc + b_all.data.set.power_limit = params.power_limit b = Bat(0) b.data.get.power = params.power data.data.bat_data["bat0"] = b @@ -151,3 +163,57 @@ def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypat # evaluation assert b_all.data.set.charging_power_left == params.expected_charging_power_left assert b_all.data.set.regulate_up == params.expected_regulate_up + + +def default_chargepoint_factory() -> List[Chargepoint]: + return [Chargepoint(3, None)] + + +@dataclass +class PowerLimitParams: + name: str + expected_power_limit_bat: Optional[float] + power_limit_mode: str = BatPowerLimitMode.NO_LIMIT.value + cps: List[Chargepoint] = field(default_factory=default_chargepoint_factory) + power_limit_controlable: bool = True + bat_power: float = -10 + evu_power: float = 200 + + +cases = [ + PowerLimitParams("keine Begrenzung", None), + PowerLimitParams("Begrenzung immer, keine LP im Sofortladen", None, cps=[], + power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), + PowerLimitParams("Begrenzung immer, Speicher nicht regelbar", None, power_limit_controlable=False, + power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), + PowerLimitParams("Begrenzung immer, Speicher lädt", None, bat_power=100, + power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), + PowerLimitParams("Begrenzung immer,Einspeisung", None, evu_power=-100, + power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), + PowerLimitParams("Begrenzung immer", 0, power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), + PowerLimitParams("Begrenzung Hausverbrauch", 456, + power_limit_mode=BatPowerLimitMode.LIMIT_TO_HOME_CONSUMPTION.value), +] + + +@pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) +def test_get_power_limit(params: PowerLimitParams, data_, monkeypatch): + b_all = BatAll() + b_all.data.config.power_limit_mode = params.power_limit_mode + b_all.data.get.power_limit_controlable = params.power_limit_controlable + b_all.data.get.power = params.bat_power + data.data.counter_all_data = hierarchy_standard() + data.data.counter_all_data.data.set.home_consumption = 456 + data.data.counter_data["counter0"].data.get.power = params.evu_power + data.data.bat_all_data = b_all + + get_chargepoints_by_chargemodes_mock = Mock(return_value=params.cps) + monkeypatch.setattr(bat_all, "get_chargepoints_by_chargemodes", get_chargepoints_by_chargemodes_mock) + get_evu_counter_mock = Mock(return_value=data.data.counter_data["counter0"]) + monkeypatch.setattr(data.data.counter_all_data, "get_evu_counter", get_evu_counter_mock) + get_controlable_bat_components_mock = Mock(return_value=[MqttBat(MqttBatSetup(id=2))]) + monkeypatch.setattr(bat_all, "get_controlable_bat_components", get_controlable_bat_components_mock) + + data.data.bat_all_data.get_power_limit() + + assert data.data.bat_data["bat2"].data.set.power_limit == params.expected_power_limit_bat diff --git a/packages/control/prepare.py b/packages/control/prepare.py index ee32f486ee..51a1466693 100644 --- a/packages/control/prepare.py +++ b/packages/control/prepare.py @@ -22,7 +22,6 @@ def setup_algorithm(self) -> None: data.data.bat_all_data.calc_power_for_all_components() for cp in data.data.cp_data.values(): cp.setup_values_at_start() - data.data.bat_all_data.setup_bat() levels = data.data.counter_all_data.get_list_of_elements_per_level() for level in reversed(levels): for element in level: @@ -30,6 +29,8 @@ def setup_algorithm(self) -> None: data.data.counter_data[f"counter{element['id']}"].setup_counter() for cp in data.data.cp_data.values(): cp.update(data.data.ev_data) + # Nach cp update, da für die Speicher-Sperre der Lademodus bekannt sein muss. + data.data.bat_all_data.setup_bat() data.data.cp_all_data.get_cp_sum() data.data.cp_all_data.no_charge() data.data.counter_all_data.set_home_consumption() diff --git a/packages/control/process.py b/packages/control/process.py index 0a742fe277..e4f4887638 100644 --- a/packages/control/process.py +++ b/packages/control/process.py @@ -4,6 +4,7 @@ import threading from typing import List +from control.bat_all import get_controlable_bat_components from control.chargelog import chargelog from control.chargepoint import chargepoint from control import data @@ -59,6 +60,12 @@ def process_algorithm_results(self) -> None: modules_threads.append(self._start_charging(cp)) except Exception: log.exception("Fehler im Process-Modul für Ladepunkt "+str(cp)) + for bat_component in get_controlable_bat_components(): + modules_threads.append( + threading.Thread( + target=bat_component.set_power_limit, + args=(data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit,), + name=f"set power limit {bat_component.component_config.id}")) if modules_threads: joined_thread_handler(modules_threads, 3) diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index 61b524ef7c..7c541c6221 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -703,6 +703,7 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage): """ try: if ("openWB/set/bat/config/configured" in msg.topic or + "openWB/set/bat/get/power_limit_controlable" in msg.topic or "openWB/set/bat/set/regulate_up" in msg.topic): self._validate_value(msg, bool) elif "openWB/set/bat/set/charging_power_left" in msg.topic: @@ -714,11 +715,13 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage): elif ("openWB/set/bat/get/imported" in msg.topic or "openWB/set/bat/get/exported" in msg.topic or "openWB/set/bat/get/daily_exported" in msg.topic or - "openWB/set/bat/get/daily_imported" in msg.topic): + "openWB/set/bat/get/daily_imported" in msg.topic or + "openWB/set/bat/set/power_limit" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) elif "openWB/set/bat/get/fault_state" in msg.topic: self._validate_value(msg, int, [(0, 2)]) - elif "openWB/set/bat/get/fault_str" in msg.topic: + elif ("openWB/set/bat/get/fault_str" in msg.topic or + "openWB/set/bat/config/power_limit_mode" in msg.topic): self._validate_value(msg, str) elif "/config" in msg.topic: self._validate_value(msg, "json") @@ -736,6 +739,10 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, int, [(0, 2)]) elif "/get/fault_str" in msg.topic: self._validate_value(msg, str) + elif "/set/power_limit_controlable" in msg.topic: + self._validate_value(msg, bool) + elif "/set/power_limit" in msg.topic: + self._validate_value(msg, float) else: self.__unknown_topic(msg) else: diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 19756641f8..97fab21478 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -52,10 +52,12 @@ class UpdateConfig: DATASTORE_VERSION = 63 valid_topic = [ "^openWB/bat/config/configured$", + "^openWB/bat/config/power_limit_mode$", "^openWB/bat/set/charging_power_left$", "^openWB/bat/set/regulate_up$", "^openWB/bat/get/fault_state$", "^openWB/bat/get/fault_str$", + "^openWB/bat/get/power_limit_controlable$", "^openWB/bat/get/soc$", "^openWB/bat/get/power$", "^openWB/bat/get/imported$", @@ -70,6 +72,8 @@ class UpdateConfig: "^openWB/bat/[0-9]+/get/daily_imported$", "^openWB/bat/[0-9]+/get/fault_state$", "^openWB/bat/[0-9]+/get/fault_str$", + "^openWB/bat/[0-9]+/get/power_limit_controlable$", + "^openWB/bat/[0-9]+/set/power_limit$", "^openWB/chargepoint/get/power$", "^openWB/chargepoint/get/exported$", @@ -432,6 +436,7 @@ class UpdateConfig: ] default_topic = ( ("openWB/bat/config/configured", False), + ("openWB/bat/config/power_limit_mode", "no_limit"), ("openWB/bat/get/fault_state", 0), ("openWB/bat/get/fault_str", NO_ERROR), ("openWB/chargepoint/get/power", 0), diff --git a/packages/modules/common/abstract_device.py b/packages/modules/common/abstract_device.py index feb80d0bb4..54698be862 100644 --- a/packages/modules/common/abstract_device.py +++ b/packages/modules/common/abstract_device.py @@ -1,5 +1,5 @@ from abc import abstractmethod -from typing import Type +from typing import Optional, Type class AbstractDevice: @@ -16,6 +16,21 @@ def update(self) -> None: pass +class AbstractBat: + @abstractmethod + def __init__(self, *kwargs) -> None: + pass + + @abstractmethod + def update(self, *kwargs) -> None: + pass + + @abstractmethod + def set_power_limit(self, power_limit: Optional[int]) -> None: + # power limit None heißt, auf maximale Speicherleistung setzen = Speicher-Begrenzung aufheben + pass + + class DeviceDescriptor: def __init__(self, configuration_factory: Type): self.configuration_factory = configuration_factory diff --git a/packages/modules/devices/alpha_ess/alpha_ess/bat.py b/packages/modules/devices/alpha_ess/alpha_ess/bat.py index 2fc7f1d7c7..4dcc0ca9e5 100644 --- a/packages/modules/devices/alpha_ess/alpha_ess/bat.py +++ b/packages/modules/devices/alpha_ess/alpha_ess/bat.py @@ -6,6 +6,7 @@ from dataclass_utils import dataclass_from_dict from modules.devices.alpha_ess.alpha_ess.config import AlphaEssBatSetup, AlphaEssConfiguration from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -16,7 +17,7 @@ log = logging.getLogger(__name__) -class AlphaEssBat: +class AlphaEssBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, AlphaEssBatSetup], tcp_client: modbus.ModbusTcpClient_, diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py index 5d5eb7a7c4..f906db80b3 100644 --- a/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.devices.azzurro_zcs.azzurro_zcs.config import ZCSBatSetup -class ZCSBat: +class ZCSBat(AbstractBat): def __init__(self, component_config: Union[Dict, ZCSBatSetup], modbus_id: int) -> None: diff --git a/packages/modules/devices/batterx/batterx/bat.py b/packages/modules/devices/batterx/batterx/bat.py index 4f9e327133..7828ea72cc 100644 --- a/packages/modules/devices/batterx/batterx/bat.py +++ b/packages/modules/devices/batterx/batterx/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.devices.batterx.batterx.config import BatterXBatSetup +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.common.store import get_bat_value_store -class BatterXBat: +class BatterXBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, BatterXBatSetup]) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(BatterXBatSetup, component_config) diff --git a/packages/modules/devices/byd/byd/bat.py b/packages/modules/devices/byd/byd/bat.py index 3154ff9160..0770b272ef 100644 --- a/packages/modules/devices/byd/byd/bat.py +++ b/packages/modules/devices/byd/byd/bat.py @@ -6,6 +6,7 @@ from dataclass_utils import dataclass_from_dict from modules.devices.byd.byd.config import BYDBatSetup from modules.common import req +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -15,7 +16,7 @@ log = logging.getLogger(__name__) -class BYDBat: +class BYDBat(AbstractBat): def __init__(self, component_config: Union[Dict, BYDBatSetup], device_config) -> None: diff --git a/packages/modules/devices/deye/deye/bat.py b/packages/modules/devices/deye/deye/bat.py index 98feab67a0..c3d98ca1ca 100644 --- a/packages/modules/devices/deye/deye/bat.py +++ b/packages/modules/devices/deye/deye/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -13,7 +14,7 @@ log = logging.getLogger(__name__) -class DeyeBat: +class DeyeBat(AbstractBat): def __init__(self, device_id: int, component_config: DeyeBatSetup) -> None: self.component_config = dataclass_from_dict(DeyeBatSetup, component_config) self.store = get_bat_value_store(self.component_config.id) diff --git a/packages/modules/devices/e3dc/e3dc/bat.py b/packages/modules/devices/e3dc/e3dc/bat.py index 04b2ce955f..bbe3d01cce 100644 --- a/packages/modules/devices/e3dc/e3dc/bat.py +++ b/packages/modules/devices/e3dc/e3dc/bat.py @@ -2,6 +2,7 @@ import logging from typing import Tuple from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.modbus import ModbusDataType, Endian @@ -22,7 +23,7 @@ def read_bat(client: modbus.ModbusTcpClient_, modbus_id: int) -> Tuple[int, int] return soc, power -class E3dcBat: +class E3dcBat(AbstractBat): def __init__(self, device_id: int, component_config: E3dcBatSetup, diff --git a/packages/modules/devices/enphase/enphase/bat.py b/packages/modules/devices/enphase/enphase/bat.py index 497e713dbd..af0d906d11 100644 --- a/packages/modules/devices/enphase/enphase/bat.py +++ b/packages/modules/devices/enphase/enphase/bat.py @@ -3,6 +3,7 @@ from typing import Any, Dict, Optional, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -13,7 +14,7 @@ log = logging.getLogger(__name__) -class EnphaseBat: +class EnphaseBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, EnphaseBatSetup]) -> None: self.component_config = dataclass_from_dict(EnphaseBatSetup, component_config) self.store = get_bat_value_store(self.component_config.id) diff --git a/packages/modules/devices/fems/fems/bat.py b/packages/modules/devices/fems/fems/bat.py index ca1e35457a..04709c0cfe 100644 --- a/packages/modules/devices/fems/fems/bat.py +++ b/packages/modules/devices/fems/fems/bat.py @@ -2,6 +2,7 @@ from requests import Session from helpermodules.scale_metric import scale_metric from modules.devices.fems.fems.config import FemsBatSetup +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ log = logging.getLogger(__name__) -class FemsBat: +class FemsBat(AbstractBat): def __init__(self, ip_address: str, component_config: FemsBatSetup, session: Session) -> None: self.ip_address = ip_address self.component_config = component_config diff --git a/packages/modules/devices/fox_ess/fox_ess/bat.py b/packages/modules/devices/fox_ess/fox_ess/bat.py index 2f6ec8ffe7..1e9f2c50e8 100644 --- a/packages/modules/devices/fox_ess/fox_ess/bat.py +++ b/packages/modules/devices/fox_ess/fox_ess/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,7 +12,7 @@ log = logging.getLogger(__name__) -class FoxEssBat: +class FoxEssBat(AbstractBat): def __init__(self, component_config: FoxEssBatSetup) -> None: self.component_config = dataclass_from_dict(FoxEssBatSetup, component_config) self.store = get_bat_value_store(self.component_config.id) diff --git a/packages/modules/devices/fronius/fronius/bat.py b/packages/modules/devices/fronius/fronius/bat.py index 3846228ff3..30c08215f0 100644 --- a/packages/modules/devices/fronius/fronius/bat.py +++ b/packages/modules/devices/fronius/fronius/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import req +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.fronius.fronius.config import FroniusConfiguration -class FroniusBat: +class FroniusBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, FroniusBatSetup], diff --git a/packages/modules/devices/generic/http/bat.py b/packages/modules/devices/generic/http/bat.py index 06ef6e217f..8b9bc5600a 100644 --- a/packages/modules/devices/generic/http/bat.py +++ b/packages/modules/devices/generic/http/bat.py @@ -4,6 +4,7 @@ from requests import Session from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -13,7 +14,7 @@ from modules.devices.generic.http.config import HttpBatSetup -class HttpBat: +class HttpBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, HttpBatSetup], url: str) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(HttpBatSetup, component_config) diff --git a/packages/modules/devices/generic/json/bat.py b/packages/modules/devices/generic/json/bat.py index b4a5fc9863..12f638c8ac 100644 --- a/packages/modules/devices/generic/json/bat.py +++ b/packages/modules/devices/generic/json/bat.py @@ -4,6 +4,7 @@ import jq from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.generic.json.config import JsonBatSetup -class JsonBat: +class JsonBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, JsonBatSetup]) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(JsonBatSetup, component_config) diff --git a/packages/modules/devices/generic/mqtt/bat.py b/packages/modules/devices/generic/mqtt/bat.py index a4c17bfc10..5f518c26e7 100644 --- a/packages/modules/devices/generic/mqtt/bat.py +++ b/packages/modules/devices/generic/mqtt/bat.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Dict, Optional, Union +from modules.common.abstract_device import AbstractBat from modules.common.fault_state import ComponentInfo, FaultState from dataclass_utils import dataclass_from_dict @@ -7,10 +8,15 @@ from modules.devices.generic.mqtt.config import MqttBatSetup -class MqttBat: +class MqttBat(AbstractBat): def __init__(self, component_config: Union[Dict, MqttBatSetup]) -> None: self.component_config = dataclass_from_dict(MqttBatSetup, component_config) self.fault_state = FaultState(ComponentInfo.from_component_config(component_config)) + def set_power_limit(self, power_limit: Optional[int]) -> None: + # wird bereits in Regelung gesetzt, eigene Implementierung notwendig, um zu erkennen, + # ob der Speicher die Funktion bietet + pass + component_descriptor = ComponentDescriptor(configuration_factory=MqttBatSetup) diff --git a/packages/modules/devices/good_we/good_we/bat.py b/packages/modules/devices/good_we/good_we/bat.py index 8830424286..f203bcfddd 100644 --- a/packages/modules/devices/good_we/good_we/bat.py +++ b/packages/modules/devices/good_we/good_we/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.modbus import ModbusDataType @@ -12,7 +13,7 @@ from modules.devices.good_we.good_we.version import GoodWeVersion -class GoodWeBat: +class GoodWeBat(AbstractBat): def __init__(self, modbus_id: int, version: GoodWeVersion, diff --git a/packages/modules/devices/huawei/huawei/bat.py b/packages/modules/devices/huawei/huawei/bat.py index fd43ae206f..1c1b480f13 100644 --- a/packages/modules/devices/huawei/huawei/bat.py +++ b/packages/modules/devices/huawei/huawei/bat.py @@ -3,6 +3,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.huawei.huawei.config import HuaweiBatSetup -class HuaweiBat: +class HuaweiBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, HuaweiBatSetup], diff --git a/packages/modules/devices/huawei/huawei_smartlogger/bat.py b/packages/modules/devices/huawei/huawei_smartlogger/bat.py index 1fff51eb10..342ee8b5dd 100644 --- a/packages/modules/devices/huawei/huawei_smartlogger/bat.py +++ b/packages/modules/devices/huawei/huawei_smartlogger/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.huawei.huawei_smartlogger.config import Huawei_SmartloggerBatSetup -class Huawei_SmartloggerBat: +class Huawei_SmartloggerBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, Huawei_SmartloggerBatSetup], diff --git a/packages/modules/devices/kostal/kostal_plenticore/bat.py b/packages/modules/devices/kostal/kostal_plenticore/bat.py index 92c643a715..2296969127 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/bat.py +++ b/packages/modules/devices/kostal/kostal_plenticore/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging from typing import Any, Callable +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.modbus import ModbusDataType @@ -12,7 +13,7 @@ log = logging.getLogger(__name__) -class KostalPlenticoreBat: +class KostalPlenticoreBat(AbstractBat): def __init__(self, device_id: int, component_config: KostalPlenticoreBatSetup) -> None: diff --git a/packages/modules/devices/lg/lg/bat.py b/packages/modules/devices/lg/lg/bat.py index bc922d368f..82d30cf11e 100644 --- a/packages/modules/devices/lg/lg/bat.py +++ b/packages/modules/devices/lg/lg/bat.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.devices.lg.lg.config import LgBatSetup -class LgBat: +class LgBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, LgBatSetup]) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(LgBatSetup, component_config) diff --git a/packages/modules/devices/mtec/mtec/bat.py b/packages/modules/devices/mtec/mtec/bat.py index d165d62406..98c2c40585 100644 --- a/packages/modules/devices/mtec/mtec/bat.py +++ b/packages/modules/devices/mtec/mtec/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ log = logging.getLogger(__name__) -class MTecBat: +class MTecBat(AbstractBat): def __init__(self, device_id: int, component_config: MTecBatSetup) -> None: self.component_config = dataclass_from_dict(MTecBatSetup, component_config) self.store = get_bat_value_store(self.component_config.id) diff --git a/packages/modules/devices/openwb/openwb_bat_kit/bat.py b/packages/modules/devices/openwb/openwb_bat_kit/bat.py index c8a793624a..b39e8b1f4c 100644 --- a/packages/modules/devices/openwb/openwb_bat_kit/bat.py +++ b/packages/modules/devices/openwb/openwb_bat_kit/bat.py @@ -2,6 +2,7 @@ from typing import Union from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_type import ComponentDescriptor from modules.devices.openwb.openwb_bat_kit.config import BatKitBatSetup from modules.devices.openwb.openwb_evu_kit.config import EvuKitBatSetup @@ -9,7 +10,7 @@ from modules.devices.openwb.openwb_flex.config import convert_to_flex_setup -class BatKit(BatKitFlex): +class BatKit(BatKitFlex, AbstractBat): def __init__(self, device_id: int, component_config: Union[BatKitBatSetup, EvuKitBatSetup], diff --git a/packages/modules/devices/openwb/openwb_flex/bat.py b/packages/modules/devices/openwb/openwb_flex/bat.py index d4df187cad..d4eb8fa39a 100644 --- a/packages/modules/devices/openwb/openwb_flex/bat.py +++ b/packages/modules/devices/openwb/openwb_flex/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -14,7 +15,7 @@ from modules.devices.openwb.openwb_flex.versions import kit_bat_version_factory -class BatKitFlex: +class BatKitFlex(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, BatKitFlexSetup], diff --git a/packages/modules/devices/qcells/qcells/bat.py b/packages/modules/devices/qcells/qcells/bat.py index 86348aecbd..ebef8b12d0 100644 --- a/packages/modules/devices/qcells/qcells/bat.py +++ b/packages/modules/devices/qcells/qcells/bat.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.devices.qcells.qcells.config import QCellsBatSetup -class QCellsBat: +class QCellsBat(AbstractBat): def __init__(self, component_config: Union[Dict, QCellsBatSetup], modbus_id: int) -> None: diff --git a/packages/modules/devices/rct/rct/bat.py b/packages/modules/devices/rct/rct/bat.py index b5f6cfb9a3..88320b3c72 100644 --- a/packages/modules/devices/rct/rct/bat.py +++ b/packages/modules/devices/rct/rct/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,7 +12,7 @@ log = logging.getLogger(__name__) -class RctBat: +class RctBat(AbstractBat): def __init__(self, component_config: RctBatSetup) -> None: self.component_config = dataclass_from_dict(RctBatSetup, component_config) self.store = get_bat_value_store(self.component_config.id) diff --git a/packages/modules/devices/saxpower/saxpower/bat.py b/packages/modules/devices/saxpower/saxpower/bat.py index b9bc55741d..9cfc481163 100644 --- a/packages/modules/devices/saxpower/saxpower/bat.py +++ b/packages/modules/devices/saxpower/saxpower/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.saxpower.saxpower.config import SaxpowerBatSetup -class SaxpowerBat: +class SaxpowerBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, SaxpowerBatSetup], diff --git a/packages/modules/devices/shelly/shelly/bat.py b/packages/modules/devices/shelly/shelly/bat.py index e113c18e36..7d4fdc91ee 100644 --- a/packages/modules/devices/shelly/shelly/bat.py +++ b/packages/modules/devices/shelly/shelly/bat.py @@ -2,6 +2,7 @@ import logging from typing import Optional from modules.common import req +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ log = logging.getLogger(__name__) -class ShellyBat: +class ShellyBat(AbstractBat): def __init__(self, device_id: int, diff --git a/packages/modules/devices/siemens/siemens/bat.py b/packages/modules/devices/siemens/siemens/bat.py index 396f739fab..ce974fbdde 100644 --- a/packages/modules/devices/siemens/siemens/bat.py +++ b/packages/modules/devices/siemens/siemens/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.siemens.siemens.config import SiemensBatSetup -class SiemensBat: +class SiemensBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, SiemensBatSetup], diff --git a/packages/modules/devices/sma/sma_sunny_boy/bat.py b/packages/modules/devices/sma/sma_sunny_boy/bat.py index cc285b27ee..da50bc7673 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/bat.py +++ b/packages/modules/devices/sma/sma_sunny_boy/bat.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.devices.sma.sma_sunny_boy.config import SmaSunnyBoyBatSetup -class SunnyBoyBat: +class SunnyBoyBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, SmaSunnyBoyBatSetup], diff --git a/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py b/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py index 18355b5c1c..b80e0864d7 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py +++ b/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,7 +12,7 @@ from modules.devices.sma.sma_sunny_boy.config import SmaSunnyBoySmartEnergyBatSetup -class SunnyBoySmartEnergyBat: +class SunnyBoySmartEnergyBat(AbstractBat): SMA_INT32_NAN = 0xFFFFFFFF # SMA uses this value to represent NaN def __init__(self, diff --git a/packages/modules/devices/sma/sma_sunny_island/bat.py b/packages/modules/devices/sma/sma_sunny_island/bat.py index c1f9cff224..30fc0479a8 100644 --- a/packages/modules/devices/sma/sma_sunny_island/bat.py +++ b/packages/modules/devices/sma/sma_sunny_island/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,7 +12,7 @@ from modules.devices.sma.sma_sunny_island.config import SmaSunnyIslandBatSetup -class SunnyIslandBat: +class SunnyIslandBat(AbstractBat): def __init__(self, component_config: Union[Dict, SmaSunnyIslandBatSetup], tcp_client: modbus.ModbusTcpClient_) -> None: diff --git a/packages/modules/devices/sofar/sofar/bat.py b/packages/modules/devices/sofar/sofar/bat.py index d97e73b557..000b64378e 100644 --- a/packages/modules/devices/sofar/sofar/bat.py +++ b/packages/modules/devices/sofar/sofar/bat.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.devices.sofar.sofar.config import SofarBatSetup -class SofarBat: +class SofarBat(AbstractBat): def __init__(self, component_config: Union[Dict, SofarBatSetup], modbus_id: int) -> None: diff --git a/packages/modules/devices/solar_watt/solar_watt/bat.py b/packages/modules/devices/solar_watt/solar_watt/bat.py index 28e84c1132..e015e3c9c1 100644 --- a/packages/modules/devices/solar_watt/solar_watt/bat.py +++ b/packages/modules/devices/solar_watt/solar_watt/bat.py @@ -3,6 +3,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -14,7 +15,7 @@ log = logging.getLogger(__name__) -class SolarWattBat: +class SolarWattBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, SolarWattBatSetup]) -> None: diff --git a/packages/modules/devices/solaredge/solaredge/bat.py b/packages/modules/devices/solaredge/solaredge/bat.py index 590435b170..59483aefa0 100644 --- a/packages/modules/devices/solaredge/solaredge/bat.py +++ b/packages/modules/devices/solaredge/solaredge/bat.py @@ -6,6 +6,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -19,7 +20,7 @@ FLOAT32_UNSUPPORTED = -0xffffff00000000000000000000000000 -class SolaredgeBat: +class SolaredgeBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, SolaredgeBatSetup], diff --git a/packages/modules/devices/solarmax/solarmax/bat.py b/packages/modules/devices/solarmax/solarmax/bat.py index a3e2a4a816..3d323c18e3 100644 --- a/packages/modules/devices/solarmax/solarmax/bat.py +++ b/packages/modules/devices/solarmax/solarmax/bat.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -9,7 +10,7 @@ from modules.devices.solarmax.solarmax.config import SolarmaxBatSetup -class SolarmaxBat: +class SolarmaxBat(AbstractBat): def __init__(self, device_id: int, component_config: SolarmaxBatSetup) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(SolarmaxBatSetup, component_config) diff --git a/packages/modules/devices/solax/solax/bat.py b/packages/modules/devices/solax/solax/bat.py index 6e5917dec2..4afe6efa65 100644 --- a/packages/modules/devices/solax/solax/bat.py +++ b/packages/modules/devices/solax/solax/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.solax.solax.config import SolaxBatSetup -class SolaxBat: +class SolaxBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, SolaxBatSetup], diff --git a/packages/modules/devices/sonnen/sonnenbatterie/bat.py b/packages/modules/devices/sonnen/sonnenbatterie/bat.py index 120de9598c..5ac3cc8bbb 100644 --- a/packages/modules/devices/sonnen/sonnenbatterie/bat.py +++ b/packages/modules/devices/sonnen/sonnenbatterie/bat.py @@ -4,6 +4,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import req +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -14,7 +15,7 @@ log = logging.getLogger(__name__) -class SonnenbatterieBat: +class SonnenbatterieBat(AbstractBat): def __init__(self, device_id: int, device_address: str, diff --git a/packages/modules/devices/studer/studer/bat.py b/packages/modules/devices/studer/studer/bat.py index beedb0a928..8505f06761 100644 --- a/packages/modules/devices/studer/studer/bat.py +++ b/packages/modules/devices/studer/studer/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.modbus import ModbusDataType @@ -11,7 +12,7 @@ from modules.devices.studer.studer.config import StuderBatSetup -class StuderBat: +class StuderBat(AbstractBat): def __init__(self, component_config: Union[Dict, StuderBatSetup], tcp_client: modbus.ModbusTcpClient_) -> None: diff --git a/packages/modules/devices/sungrow/sungrow/bat.py b/packages/modules/devices/sungrow/sungrow/bat.py index 1c42d1d99d..c85f1910b3 100644 --- a/packages/modules/devices/sungrow/sungrow/bat.py +++ b/packages/modules/devices/sungrow/sungrow/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -13,7 +14,7 @@ from modules.devices.sungrow.sungrow.version import Version -class SungrowBat: +class SungrowBat(AbstractBat): def __init__(self, device_config: Union[Dict, Sungrow], component_config: Union[Dict, SungrowBatSetup], diff --git a/packages/modules/devices/tesla/tesla/bat.py b/packages/modules/devices/tesla/tesla/bat.py index a7f4449ccf..8383a4aaad 100644 --- a/packages/modules/devices/tesla/tesla/bat.py +++ b/packages/modules/devices/tesla/tesla/bat.py @@ -2,6 +2,7 @@ from typing import Dict, Union from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -10,7 +11,7 @@ from modules.devices.tesla.tesla.config import TeslaBatSetup -class TeslaBat: +class TeslaBat(AbstractBat): def __init__(self, component_config: Union[Dict, TeslaBatSetup]) -> None: self.component_config = dataclass_from_dict(TeslaBatSetup, component_config) self.store = get_bat_value_store(self.component_config.id) diff --git a/packages/modules/devices/varta/varta/bat_api.py b/packages/modules/devices/varta/varta/bat_api.py index c92d4094c0..021c250ded 100644 --- a/packages/modules/devices/varta/varta/bat_api.py +++ b/packages/modules/devices/varta/varta/bat_api.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import req +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,7 +12,7 @@ from modules.devices.varta.varta.config import VartaBatApiSetup -class VartaBatApi: +class VartaBatApi(AbstractBat): def __init__(self, device_id: int, component_config: VartaBatApiSetup, device_address: str) -> None: self.__device_id = device_id self.component_config = dataclass_from_dict(VartaBatApiSetup, component_config) diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index 3db17fff9d..8267991145 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -9,7 +10,7 @@ from modules.devices.varta.varta.config import VartaBatModbusSetup -class VartaBatModbus: +class VartaBatModbus(AbstractBat): def __init__(self, device_id: int, component_config: VartaBatModbusSetup, modbus_id: int) -> None: diff --git a/packages/modules/devices/victron/victron/bat.py b/packages/modules/devices/victron/victron/bat.py index e012aad359..0e58b5fd7f 100644 --- a/packages/modules/devices/victron/victron/bat.py +++ b/packages/modules/devices/victron/victron/bat.py @@ -3,6 +3,7 @@ from dataclass_utils import dataclass_from_dict from modules.common import modbus +from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -12,7 +13,7 @@ from modules.devices.victron.victron.config import VictronBatSetup -class VictronBat: +class VictronBat(AbstractBat): def __init__(self, device_id: int, component_config: Union[Dict, VictronBatSetup],