diff --git a/packages/control/algorithm/additional_current.py b/packages/control/algorithm/additional_current.py index 1e76a3f52a..1cc76de58e 100644 --- a/packages/control/algorithm/additional_current.py +++ b/packages/control/algorithm/additional_current.py @@ -2,7 +2,8 @@ from control.algorithm import common from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT -from control.loadmanagement import LimitingValue, Loadmanagement +from control.limiting_value import LoadmanagementLimit +from control.loadmanagement import Loadmanagement from control.chargepoint.chargepoint import Chargepoint from control.algorithm.filter_chargepoints import (get_chargepoints_by_mode_and_counter, get_preferenced_chargepoint_charging) @@ -28,7 +29,7 @@ def set_additional_current(self) -> None: missing_currents, counts = common.get_missing_currents_left(preferenced_chargepoints) available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter, cp) log.debug(f"cp {cp.num} available currents {available_currents} missing currents " - f"{missing_currents} limit {limit}") + f"{missing_currents} limit {limit.message}") cp.data.control_parameter.limit = limit available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents) current = common.get_current_to_set( @@ -46,7 +47,7 @@ def set_additional_current(self) -> None: # tested def _set_loadmangement_message(self, current: float, - limit: LimitingValue, + limit: LoadmanagementLimit, chargepoint: Chargepoint) -> None: # Strom muss an diesem Zähler geändert werden log.debug( @@ -57,4 +58,4 @@ def _set_loadmangement_message(self, round(current, 2) != round(max( chargepoint.data.control_parameter.required_currents), 2)): chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden" - f"{limit}") + f"{limit.message}") diff --git a/packages/control/algorithm/additional_current_test.py b/packages/control/algorithm/additional_current_test.py index c6ff09a0b4..35ac25410a 100644 --- a/packages/control/algorithm/additional_current_test.py +++ b/packages/control/algorithm/additional_current_test.py @@ -6,22 +6,23 @@ from control.chargepoint.control_parameter import ControlParameter from control.ev.charge_template import ChargeTemplate from control.ev.ev import Ev +from control.limiting_value import LoadmanagementLimit from control.loadmanagement import LimitingValue @pytest.mark.parametrize( "set_current, limit, expected_msg", - [pytest.param(7, None, None, id="unverändert"), + [pytest.param(7, LoadmanagementLimit(None, None), None, id="unverändert"), pytest.param( - 6, LimitingValue.CURRENT.value.format('Garage'), + 6, LoadmanagementLimit(LimitingValue.CURRENT.value.format('Garage'), LimitingValue.CURRENT), f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden{LimitingValue.CURRENT.value.format('Garage')}", id="begrenzt durch Strom"), pytest.param( - 6, LimitingValue.POWER.value.format('Garage'), + 6, LoadmanagementLimit(LimitingValue.POWER.value.format('Garage'), LimitingValue.POWER), f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden{LimitingValue.POWER.value.format('Garage')}", id="begrenzt durch Leistung"), pytest.param( - 6, LimitingValue.UNBALANCED_LOAD.value.format('Garage'), + 6, LoadmanagementLimit(LimitingValue.UNBALANCED_LOAD.value.format('Garage'), LimitingValue.UNBALANCED_LOAD), f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden" f"{LimitingValue.UNBALANCED_LOAD.value.format('Garage')}", id="begrenzt durch Schieflast"), diff --git a/packages/control/algorithm/min_current.py b/packages/control/algorithm/min_current.py index bcb81468dc..ff24452cba 100644 --- a/packages/control/algorithm/min_current.py +++ b/packages/control/algorithm/min_current.py @@ -34,7 +34,7 @@ def set_min_current(self) -> None: common.set_current_counterdiff(-(cp.data.set.current or 0), 0, cp) if limit: cp.set_state_and_log( - f"Ladung kann nicht gestartet werden{limit}") + f"Ladung kann nicht gestartet werden{limit.message}") else: common.set_current_counterdiff( cp.data.set.target_current, diff --git a/packages/control/algorithm/surplus_controlled.py b/packages/control/algorithm/surplus_controlled.py index 88da8bcf93..2f7cdf39ab 100644 --- a/packages/control/algorithm/surplus_controlled.py +++ b/packages/control/algorithm/surplus_controlled.py @@ -11,6 +11,7 @@ from control.chargepoint.chargepoint import Chargepoint from control.chargepoint.chargepoint_state import ChargepointState, CHARGING_STATES from control.counter import ControlRangeState, Counter +from control.limiting_value import LoadmanagementLimit from control.loadmanagement import LimitingValue, Loadmanagement @@ -82,16 +83,16 @@ def _set(self, def _set_loadmangement_message(self, current: float, - limit: LimitingValue, + limit: LoadmanagementLimit, chargepoint: Chargepoint) -> None: # Strom muss an diesem Zähler geändert werden if (current != chargepoint.data.set.current and # Strom erreicht nicht die vorgegebene Stromstärke current != max(chargepoint.data.control_parameter.required_currents) and # im PV-Laden wird der Strom immer durch die Leistung begrenzt - limit != LimitingValue.POWER): + limit.limiting_value != LimitingValue.POWER): chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden" - f"{limit}") + f"{limit.message}") # tested def filter_by_feed_in_limit(self, chargepoints: List[Chargepoint]) -> Tuple[List[Chargepoint], List[Chargepoint]]: diff --git a/packages/control/auto_phase_switch_test.py b/packages/control/auto_phase_switch_test.py index 25531d2e96..a7ae74224e 100644 --- a/packages/control/auto_phase_switch_test.py +++ b/packages/control/auto_phase_switch_test.py @@ -5,6 +5,7 @@ from control.chargepoint.control_parameter import ControlParameter from control.counter import Counter, CounterData, Set +from control.limiting_value import LoadmanagementLimit from control.pv_all import PvAll from control.bat_all import BatAll from control.general import General @@ -137,7 +138,7 @@ def test_auto_phase_switch(monkeypatch, vehicle: Ev, params: Params): params.get_power, 32, 3, - None) + LoadmanagementLimit(None, None)) # evaluation assert phases_to_use == params.expected_phases_to_use diff --git a/packages/control/chargepoint/control_parameter.py b/packages/control/chargepoint/control_parameter.py index f419f4f012..4b153067d0 100644 --- a/packages/control/chargepoint/control_parameter.py +++ b/packages/control/chargepoint/control_parameter.py @@ -3,7 +3,7 @@ from control.chargepoint.chargepoint_state import ChargepointState from control.chargemode import Chargemode as Chargemode_enum -from control.limiting_value import LimitingValue +from control.limiting_value import LoadmanagementLimit, loadmanagement_limit_factory from dataclass_utils.factories import currents_list_factory @@ -17,7 +17,8 @@ class ControlParameter: default=None, metadata={"topic": "control_parameter/imported_at_plan_start"}) imported_instant_charging: Optional[float] = field( default=None, metadata={"topic": "control_parameter/imported_instant_charging"}) - limit: Optional[LimitingValue] = field(default=None, metadata={"topic": "control_parameter/limit"}) + limit: Optional[LoadmanagementLimit] = field(default_factory=loadmanagement_limit_factory, metadata={ + "topic": "control_parameter/limit"}) min_current: int = field(default=6, metadata={"topic": "control_parameter/min_current"}) phases: int = field(default=0, metadata={"topic": "control_parameter/phases"}) prio: bool = field(default=False, metadata={"topic": "control_parameter/prio"}) diff --git a/packages/control/ev/ev.py b/packages/control/ev/ev.py index 5f22826304..476157a903 100644 --- a/packages/control/ev/ev.py +++ b/packages/control/ev/ev.py @@ -8,7 +8,6 @@ """ from dataclasses import dataclass, field import logging -import re from typing import List, Optional, Tuple from control import data @@ -17,7 +16,7 @@ from control.chargepoint.control_parameter import ControlParameter from control.ev.charge_template import ChargeTemplate from control.ev.ev_template import EvTemplate -from control.limiting_value import LimitingValue +from control.limiting_value import LimitingValue, LoadmanagementLimit from dataclass_utils.factories import empty_list_factory from helpermodules import timecheck from helpermodules.constants import NO_ERROR @@ -294,7 +293,7 @@ def _check_phase_switch_conditions(self, get_currents: List[float], get_power: float, max_current_cp: int, - limit: LimitingValue) -> Tuple[bool, Optional[str]]: + limit: LoadmanagementLimit) -> Tuple[bool, Optional[str]]: # Manche EV laden mit 6.1A bei 6A Soll-Strom min_current = (max(control_parameter.min_current, control_parameter.required_current) + self.ev_template.data.nominal_difference) @@ -309,9 +308,7 @@ def _check_phase_switch_conditions(self, feed_in_yield = 0 all_surplus = data.data.counter_all_data.get_evu_counter().get_usable_surplus(feed_in_yield) required_surplus = control_parameter.min_current * max_phases_ev * 230 - get_power - unblanced_load_limit_reached = ( - limit is not None and - re.search(re.escape(LimitingValue.UNBALANCED_LOAD.value).replace(r'\{\}', r'.+'), limit)) + unblanced_load_limit_reached = limit.limiting_value == LimitingValue.UNBALANCED_LOAD condition_1_to_3 = (((max(get_currents) > max_current and all_surplus > required_surplus) or unblanced_load_limit_reached) and phases_in_use == 1) @@ -335,7 +332,7 @@ def auto_phase_switch(self, get_power: float, max_current_cp: int, max_phases: int, - limit: LimitingValue) -> Tuple[int, float, Optional[str]]: + limit: LoadmanagementLimit) -> Tuple[int, float, Optional[str]]: message = None timestamp_last_phase_switch = control_parameter.timestamp_last_phase_switch current = control_parameter.required_current diff --git a/packages/control/limiting_value.py b/packages/control/limiting_value.py index 6a12065e71..7f83e0b3ec 100644 --- a/packages/control/limiting_value.py +++ b/packages/control/limiting_value.py @@ -1,4 +1,6 @@ +from dataclasses import dataclass from enum import Enum +from typing import Optional class LimitingValue(Enum): @@ -11,3 +13,13 @@ class LimitingValue(Enum): "reduziert wird.") CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nicht " "gesteuert werden können. Bitte prüfe die Status-Seite.") + + +@dataclass +class LoadmanagementLimit: + message: Optional[str] + limiting_value: Optional[LimitingValue] + + +def loadmanagement_limit_factory() -> LoadmanagementLimit: + return LoadmanagementLimit(None, None) diff --git a/packages/control/loadmanagement.py b/packages/control/loadmanagement.py index ae2108d2f8..0ad4b27350 100644 --- a/packages/control/loadmanagement.py +++ b/packages/control/loadmanagement.py @@ -5,7 +5,7 @@ from control import data from control.chargepoint.chargepoint import Chargepoint from control.counter import Counter -from control.limiting_value import LimitingValue +from control.limiting_value import LimitingValue, LoadmanagementLimit from modules.common.utils.component_parser import get_component_name_by_id @@ -17,72 +17,74 @@ def get_available_currents(self, missing_currents: List[float], counter: Counter, cp: Chargepoint, - feed_in: int = 0) -> Tuple[List[float], Optional[str]]: + feed_in: int = 0) -> Tuple[List[float], LoadmanagementLimit]: raw_currents_left = counter.data.set.raw_currents_left try: available_currents, limit = self._limit_by_dimming_via_direct_control(missing_currents, cp) available_currents, new_limit = self._limit_by_dimming(available_currents, cp) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit available_currents, new_limit = self._limit_by_ripple_control_receiver(available_currents, cp) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit except ValueError as e: available_currents = [0]*3 - limit = e.args[0] + limit = LoadmanagementLimit(e.args[0], e) available_currents, new_limit = self._limit_by_current(counter, available_currents, raw_currents_left) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit available_currents, new_limit = self._limit_by_power( counter, available_currents, counter.data.set.raw_power_left, feed_in) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit if f"counter{counter.num}" == data.data.counter_all_data.get_evu_counter_str(): available_currents, new_limit = self._limit_by_unbalanced_load( counter, available_currents, raw_currents_left, len([value for value in missing_currents if value != 0])) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit return available_currents, limit def get_available_currents_surplus(self, missing_currents: List[float], counter: Counter, cp: Chargepoint, - feed_in: int = 0) -> Tuple[List[float], Optional[str]]: + feed_in: int = 0) -> Tuple[List[float], LoadmanagementLimit]: raw_currents_left = counter.data.set.raw_currents_left available_currents, limit = self._limit_by_dimming_via_direct_control(missing_currents, cp) available_currents, new_limit = self._limit_by_ripple_control_receiver(available_currents, cp) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit available_currents, new_limit = self._limit_by_current(counter, available_currents, raw_currents_left) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit available_currents, new_limit = self._limit_by_power( counter, available_currents, counter.data.set.surplus_power_left, feed_in) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit if f"counter{counter.num}" == data.data.counter_all_data.get_evu_counter_str(): available_currents, new_limit = self._limit_by_unbalanced_load( counter, available_currents, raw_currents_left, len([value for value in missing_currents if value != 0])) - limit = new_limit if new_limit is not None else limit + limit = new_limit if new_limit.limiting_value is not None else limit return available_currents, limit def _limit_by_unbalanced_load(self, counter: Counter, available_currents: List[float], raw_currents_left: List[float], - phases_to_use: int) -> Tuple[List[float], Optional[str]]: + phases_to_use: int) -> Tuple[List[float], LoadmanagementLimit]: raw_currents_left_charging = list(map(operator.sub, raw_currents_left, available_currents)) max_exceeding = counter.get_unbalanced_load_exceeding(raw_currents_left_charging) - limit = None + limit = LoadmanagementLimit(None, None) if max(max_exceeding) > 0: if phases_to_use < 3 and phases_to_use > 0: available_currents = list(map(operator.sub, available_currents, max_exceeding)) log.debug(f"Schieflast {max_exceeding}A korrigieren: {available_currents}") - limit = LimitingValue.UNBALANCED_LOAD.value.format(get_component_name_by_id(counter.num)) + limit = LoadmanagementLimit( + LimitingValue.UNBALANCED_LOAD.value.format(get_component_name_by_id(counter.num)), + LimitingValue.UNBALANCED_LOAD) elif phases_to_use == 3: log.debug("Schieflastkorrektur nicht möglich, da alle Phasen genutzt werden.") return available_currents, limit @@ -92,9 +94,9 @@ def _limit_by_power(self, counter: Counter, available_currents: List[float], raw_power_left: Optional[float], - feed_in: Optional[float]) -> Tuple[List[float], Optional[str]]: + feed_in: Optional[float]) -> Tuple[List[float], LoadmanagementLimit]: currents = available_currents.copy() - limit = None + limit = LoadmanagementLimit(None, None) if raw_power_left: if feed_in: raw_power_left = raw_power_left - feed_in @@ -104,39 +106,43 @@ def _limit_by_power(self, # Am meisten belastete Phase trägt am meisten zur Leistungsreduktion bei. currents[i] = available_currents[i] / sum(available_currents) * raw_power_left / 230 log.debug(f"Leistungsüberschreitung auf {raw_power_left}W korrigieren: {available_currents}") - limit = LimitingValue.POWER.value.format(get_component_name_by_id(counter.num)) + limit = LoadmanagementLimit(LimitingValue.POWER.value.format(get_component_name_by_id(counter.num)), + LimitingValue.POWER) return currents, limit # tested def _limit_by_current(self, counter: Counter, missing_currents: List[float], - raw_currents_left: List[float]) -> Tuple[List[float], Optional[str]]: + raw_currents_left: List[float]) -> Tuple[List[float], LoadmanagementLimit]: available_currents = [0.0]*3 - limit = None + limit = LoadmanagementLimit(None, None) for i in range(0, 3): available_currents[i] = min(missing_currents[i], raw_currents_left[i]) if available_currents != missing_currents: log.debug(f"Stromüberschreitung {missing_currents}W korrigieren: {available_currents}") - limit = LimitingValue.CURRENT.value.format(get_component_name_by_id(counter.num)) + limit = LoadmanagementLimit(LimitingValue.CURRENT.value.format(get_component_name_by_id(counter.num)), + LimitingValue.CURRENT) return available_currents, limit def _limit_by_dimming_via_direct_control(self, missing_currents: List[float], - cp: Chargepoint) -> Tuple[List[float], Optional[str]]: + cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: if data.data.io_actions.dimming_via_direct_control({"type": "cp", "id": cp.num}): phases = 3-missing_currents.count(0) current_per_phase = 4200 / 230 / phases available_currents = [current_per_phase - cp.data.set.target_current if c > 0 else 0 for c in missing_currents] log.debug(f"Dimmung per Direkt-Steuerung: {available_currents}A") - return available_currents, LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value + limit = LoadmanagementLimit(LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value, + LimitingValue.DIMMING_VIA_DIRECT_CONTROL) + return available_currents, limit else: - return missing_currents, None + return missing_currents, LoadmanagementLimit(None, None) def _limit_by_dimming(self, available_currents: List[float], - cp: Chargepoint) -> Tuple[List[float], Optional[str]]: + cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: dimming_power_left = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num}) if dimming_power_left: if sum(available_currents)*230 > dimming_power_left: @@ -144,12 +150,12 @@ def _limit_by_dimming(self, 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, LimitingValue.DIMMING.value - return available_currents, None + return available_currents, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING) + return available_currents, LoadmanagementLimit(None, None) def _limit_by_ripple_control_receiver(self, available_currents: List[float], - cp: Chargepoint) -> Tuple[List[float], Optional[str]]: + cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: value = data.data.io_actions.ripple_control_receiver({"type": "cp", "id": cp.num}) if value != 1: phases = 3-available_currents.count(0) @@ -162,6 +168,9 @@ def _limit_by_ripple_control_receiver(self, available_currents = [min(max_current*value - cp.data.set.target_current, c) if c > 0 else 0 for c in available_currents] log.debug(f"Reduzierung durch RSE-Kontakt auf {value*100}%, maximal {max_current*value}A") - return available_currents, LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(value*100) + limit = LoadmanagementLimit( + LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(value*100), + LimitingValue.RIPPLE_CONTROL_RECEIVER) + return available_currents, limit else: - return available_currents, None + return available_currents, LoadmanagementLimit(None, None) diff --git a/packages/control/loadmanagement_test.py b/packages/control/loadmanagement_test.py index 7bc16e0495..8cc5f526be 100644 --- a/packages/control/loadmanagement_test.py +++ b/packages/control/loadmanagement_test.py @@ -5,6 +5,7 @@ from control import loadmanagement from control.counter import Counter +from control.limiting_value import LoadmanagementLimit from control.loadmanagement import LimitingValue, Loadmanagement COUNTER_NAME = "test counter" @@ -13,11 +14,13 @@ @pytest.mark.parametrize( "available_currents, raw_power_left, expected_currents", [ - pytest.param([5, 10, 15], 6900, ([5, 10, 15], None)), + pytest.param([5, 10, 15], 6900, ([5, 10, 15], LoadmanagementLimit(None, None))), pytest.param([5, 10, 25], 1000, ([0.5434782608695652, 1.0869565217391304, - 2.717391304347826], LimitingValue.POWER.value.format(COUNTER_NAME))), + 2.717391304347826], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), + LimitingValue.POWER))), pytest.param([5, 10, 25], 5000, ([2.717391304347826, 5.434782608695652, - 13.58695652173913], LimitingValue.POWER.value.format(COUNTER_NAME))), + 13.58695652173913], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), + LimitingValue.POWER))), ]) def test_limit_by_power(available_currents: List[float], raw_power_left: float, @@ -36,8 +39,9 @@ def test_limit_by_power(available_currents: List[float], @pytest.mark.parametrize( "missing_currents, raw_currents_left, expected_currents", [ - pytest.param([5, 10, 15], [20]*3, ([5, 10, 15], None)), - pytest.param([5, 10, 15], [5, 8, 5], ([5, 8, 5], LimitingValue.CURRENT.value.format(COUNTER_NAME))), + pytest.param([5, 10, 15], [20]*3, ([5, 10, 15], LoadmanagementLimit(None, None))), + pytest.param([5, 10, 15], [5, 8, 5], ([5, 8, 5], LoadmanagementLimit( + LimitingValue.CURRENT.value.format(COUNTER_NAME), LimitingValue.CURRENT))), ]) def test_limit_by_current( missing_currents: List[float], raw_currents_left: List[float], expected_currents: List[float], monkeypatch): diff --git a/packages/dataclass_utils/_dataclass_from_dict.py b/packages/dataclass_utils/_dataclass_from_dict.py index 46b7598d72..069431311c 100644 --- a/packages/dataclass_utils/_dataclass_from_dict.py +++ b/packages/dataclass_utils/_dataclass_from_dict.py @@ -1,3 +1,4 @@ +from enum import Enum import inspect from inspect import FullArgSpec, isclass import typing @@ -45,11 +46,15 @@ def _get_argument_value(arg_spec: FullArgSpec, index: int, parameters: dict): def _dataclass_from_dict_recurse(value, requested_type: Type[T]): - return dataclass_from_dict(requested_type, value) \ - if isinstance(value, dict) and not ( + if isinstance(value, dict) and not ( _is_optional_of_dict(requested_type) or - issubclass(requested_type if isclass(requested_type) else type(bool), dict)) \ - else value + issubclass(requested_type if isclass(requested_type) else type(bool), dict)): + return dataclass_from_dict(requested_type, value) + else: + if isinstance(requested_type, type) and issubclass(requested_type, Enum): + return requested_type(value) + else: + return value def _is_optional_of_dict(requested_type): diff --git a/packages/helpermodules/changed_values_handler.py b/packages/helpermodules/changed_values_handler.py index 5f202e1980..de14759ea0 100644 --- a/packages/helpermodules/changed_values_handler.py +++ b/packages/helpermodules/changed_values_handler.py @@ -1,4 +1,4 @@ -from dataclasses import asdict, fields, is_dataclass +from dataclasses import fields, is_dataclass from enum import Enum import logging import threading @@ -6,6 +6,7 @@ from control import data from control.data import Data +from dataclass_utils._dataclass_asdict import asdict from helpermodules.pub import Pub diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index 6b31c19f9e..2f3b090e55 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -572,9 +572,10 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): elif "/control_parameter/failed_phase_switches" in msg.topic: self._validate_value(msg, int, [(0, 4)]) elif ("/control_parameter/submode" in msg.topic or - "/control_parameter/limit" in msg.topic or "/control_parameter/chargemode" in msg.topic): self._validate_value(msg, str) + elif "/control_parameter/limit" in msg.topic: + self._validate_value(msg, "json") elif "/control_parameter/prio" in msg.topic: self._validate_value(msg, bool) elif "/control_parameter/current_plan" in msg.topic: diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index f843a7ceb2..70648712f1 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -18,6 +18,7 @@ from control.ev.charge_template import ChargeTemplate, ChargeTemplateData from control.ev import ev from control.ev.ev_template import EvTemplate, EvTemplateData +from control.limiting_value import LoadmanagementLimit from control.optional_data import Ocpp from helpermodules import graph, system from helpermodules.abstract_plans import AutolockPlan, ScheduledChargingPlan, TimeChargingPlan @@ -457,7 +458,12 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg elif re.search("/chargepoint/[0-9]+/config$", msg.topic) is not None: self.process_chargepoint_config_topic(var, msg) elif re.search("/chargepoint/[0-9]+/control_parameter/", msg.topic) is not None: - self.set_json_payload_class(var["cp"+index].chargepoint.data.control_parameter, msg) + if re.search("/chargepoint/[0-9]+/control_parameter/limit", msg.topic) is not None: + payload = decode_payload(msg.payload) + var["cp"+index].chargepoint.data.control_parameter.limit = dataclass_from_dict( + LoadmanagementLimit, payload) + else: + self.set_json_payload_class(var["cp"+index].chargepoint.data.control_parameter, msg) elif re.search("/chargepoint/get/", msg.topic) is not None: self.set_json_payload_class(self.cp_all_data.data.get, msg) except Exception: diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 992ad9c80b..fe9eff7894 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -10,6 +10,7 @@ from typing import List, Optional from paho.mqtt.client import Client as MqttClient, MQTTMessage +from control.limiting_value import LoadmanagementLimit import dataclass_utils from control.chargepoint.chargepoint_template import get_chargepoint_template_default @@ -52,7 +53,7 @@ class UpdateConfig: - DATASTORE_VERSION = 76 + DATASTORE_VERSION = 77 valid_topic = [ "^openWB/bat/config/configured$", "^openWB/bat/config/power_limit_mode$", @@ -2008,3 +2009,10 @@ def upgrade(topic: str, payload) -> Optional[dict]: 'openWB/io/action/0/config': dataclass_utils.asdict(action)} self._loop_all_received_topics(upgrade) self.__update_topic("openWB/system/datastore_version", 76) + + def upgrade_datastore_76(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("openWB/chargepoint/[0-9]+/control_parameter/limit", topic) is not None: + return {topic: dataclass_utils.asdict(LoadmanagementLimit(None, None))} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 76)