diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index cd2f1d9d3f..cf24e0faa0 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -14,7 +14,6 @@ Tag-Liste: Tags, mit denen der Ladepunkt freigeschaltet werden kann. Ist diese leer, kann mit jedem Tag der Ladepunkt freigeschaltet werden. """ -import copy from dataclasses import asdict import dataclasses import logging @@ -35,7 +34,6 @@ from control.ev.ev import Ev from control import phase_switch from control.chargepoint.chargepoint_state import CHARGING_STATES, ChargepointState -from helpermodules.abstract_plans import ScheduledChargingPlan, TimeChargingPlan from helpermodules.broker import BrokerClient from helpermodules.phase_mapping import convert_single_evu_phase_to_cp_phase from helpermodules.pub import Pub @@ -800,41 +798,8 @@ def __get_payload(client, userdata, msg): Pub().pub(topic, "") def update_charge_template(self, charge_template: ChargeTemplate) -> None: - self._clear_template_topics(f'openWB/chargepoint/{self.num}/set/charge_template/#') - self.data.set.charge_template = copy.deepcopy(charge_template) - pub_template = copy.deepcopy(self.data.set.charge_template.data) - pub_template = dataclasses.asdict(pub_template) - pub_template["chargemode"]["scheduled_charging"]["plans"].clear() - pub_template["time_charging"]["plans"].clear() - Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template", pub_template) - for id, plan in self.data.set.charge_template.data.time_charging.plans.items(): - Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template/time_charging/plans/{id}", - dataclasses.asdict(plan)) - for id, plan in self.data.set.charge_template.data.chargemode.scheduled_charging.plans.items(): - Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template/chargemode/scheduled_charging/plans/{id}", - dataclasses.asdict(plan)) - - def update_bare_charge_template(self, charge_template: ChargeTemplate) -> None: - self._clear_template_topics(f"openWB/chargepoint/{self.num}/set/charge_template") - self.data.set.charge_template = copy.deepcopy(charge_template) - pub_template = copy.deepcopy(self.data.set.charge_template.data) - pub_template = dataclasses.asdict(pub_template) - pub_template["chargemode"]["scheduled_charging"]["plans"].clear() - pub_template["time_charging"]["plans"].clear() - Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template", pub_template) - - def update_charge_template_scheduled_plan(self, plan: ScheduledChargingPlan) -> None: - self._clear_template_topics( - f"openWB/chargepoint/{self.num}/set/charge_template/chargemode/scheduled_charging/plans/{plan.id}") - Pub().pub(f"openWB/set/chargepoint/{self.num}" - f"/set/charge_template/chargemode/scheduled_charging/plans/{plan.id}", - dataclasses.asdict(plan)) - - def update_charge_template_time_plan(self, plan: TimeChargingPlan) -> None: - self._clear_template_topics( - f"openWB/chargepoint/{self.num}/set/charge_template/time_charging/plans/{plan.id}") - Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template/time_charging/plans/{plan.id}", - dataclasses.asdict(plan)) + Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template", + dataclasses.asdict(charge_template.data)) def _pub_connected_vehicle(self, vehicle: Ev): """ published die Daten, die zur Anzeige auf der Hauptseite benötigt werden. @@ -903,11 +868,15 @@ def chargemode_support_phase_switch(self) -> bool: control_parameter.chargemode == Chargemode.ECO_CHARGING) and control_parameter.submode == Chargemode.PV_CHARGING and self.data.set.charge_template.data.chargemode.pv_charging.phases_to_use == 0) + for p in self.data.set.charge_template.data.chargemode.scheduled_charging.plans: + if p.id == self.data.control_parameter.current_plan: + phases_to_use_pv = p.phases_to_use_pv + else: + phases_to_use_pv = 1 scheduled_auto_switch = ( control_parameter.chargemode == Chargemode.SCHEDULED_CHARGING and control_parameter.submode == Chargemode.PV_CHARGING and - self.data.set.charge_template.data.chargemode.scheduled_charging.plans[ - str(self.data.control_parameter.current_plan)].phases_to_use_pv == 0) + phases_to_use_pv == 0) return (pv_auto_switch or scheduled_auto_switch) def failed_phase_switches_reached(self) -> bool: diff --git a/packages/control/chargepoint/chargepoint_template.py b/packages/control/chargepoint/chargepoint_template.py index 1e70a34af4..f028416714 100644 --- a/packages/control/chargepoint/chargepoint_template.py +++ b/packages/control/chargepoint/chargepoint_template.py @@ -1,12 +1,12 @@ from dataclasses import asdict, dataclass, field import logging import traceback -from typing import Dict, List +from typing import List from control import data from control.ev import ev as ev_module from control.chargepoint.charging_type import ChargingType -from dataclass_utils.factories import empty_dict_factory, empty_list_factory +from dataclass_utils.factories import empty_list_factory from helpermodules.abstract_plans import AutolockPlan from helpermodules import timecheck @@ -16,18 +16,13 @@ def get_chargepoint_template_default(): default = asdict(CpTemplateData()) - default["autolock"].pop("plans") return default -def get_autolock_plan_default(): - return asdict(AutolockPlan()) - - @dataclass class Autolock: active: bool = False - plans: Dict[int, AutolockPlan] = field(default_factory=empty_dict_factory) + plans: List[AutolockPlan] = field(default_factory=empty_list_factory) wait_for_charging_end: bool = False diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index e1b822304d..d54b2e57a8 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -2,43 +2,39 @@ import datetime import logging import traceback -from typing import Optional, Tuple +from typing import List, Optional, Tuple from control import data from control.chargepoint.chargepoint_state import CHARGING_STATES from control.chargepoint.charging_type import ChargingType from control.chargepoint.control_parameter import ControlParameter from control.ev.ev_template import EvTemplate -from dataclass_utils.factories import empty_dict_factory -from helpermodules.abstract_plans import Limit, limit_factory, ScheduledChargingPlan +from dataclass_utils.factories import empty_list_factory +from helpermodules.abstract_plans import Limit, TimeChargingPlan, limit_factory, ScheduledChargingPlan from helpermodules import timecheck log = logging.getLogger(__name__) def get_new_charge_template() -> dict: ct_default = asdict(ChargeTemplateData()) - ct_default["chargemode"]["scheduled_charging"].pop("plans") - ct_default["time_charging"].pop("plans") return ct_default def get_charge_template_default() -> dict: ct_default = asdict(ChargeTemplateData(name="Standard-Lade-Profil")) - ct_default["chargemode"]["scheduled_charging"].pop("plans") - ct_default["time_charging"].pop("plans") return ct_default @dataclass class ScheduledCharging: - plans: dict = field(default_factory=empty_dict_factory, metadata={ + plans: List[ScheduledChargingPlan] = field(default_factory=empty_list_factory, metadata={ "topic": ""}) # Dict[int,ScheduledChargingPlan] wird bei der dict to dataclass Konvertierung nicht unterstützt @dataclass class TimeCharging: active: bool = False - plans: dict = field(default_factory=empty_dict_factory, metadata={ + plans: List[TimeChargingPlan] = field(default_factory=empty_list_factory, metadata={ "topic": ""}) # Dict[int, TimeChargingPlan] wird bei der dict to dataclass Konvertierung nicht unterstützt @@ -320,7 +316,7 @@ def scheduled_charging_recent_plan(self, control_parameter: ControlParameter, soc_request_interval_offset: int) -> Optional[SelectedPlan]: plans_diff_end_date = [] - for p in self.data.chargemode.scheduled_charging.plans.values(): + for p in self.data.chargemode.scheduled_charging.plans: if p.active: if p.limit.selected == "soc" and soc is None: raise ValueError("Um Zielladen mit SoC-Ziel nutzen zu können, bitte ein SoC-Modul konfigurieren " @@ -347,7 +343,9 @@ def scheduled_charging_recent_plan(self, plan_id = list(plan_dict.keys())[0] plan_end_time = list(plan_dict.values())[0] - plan = self.data.chargemode.scheduled_charging.plans[str(plan_id)] + for p in self.data.chargemode.scheduled_charging.plans: + if p.id == plan_id: + plan = p remaining_time, missing_amount, phases, duration = self._calc_remaining_time( plan, plan_end_time, soc, ev_template, used_amount, max_hw_phases, phase_switch_supported, diff --git a/packages/control/ev/charge_template_test.py b/packages/control/ev/charge_template_test.py index 8c09973a89..c9ba2ef6b6 100644 --- a/packages/control/ev/charge_template_test.py +++ b/packages/control/ev/charge_template_test.py @@ -199,7 +199,7 @@ def test_scheduled_charging_recent_plan(end_time_mock, plan_mock_0 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=0, limit=Limit(selected="amount")) plan_mock_1 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=1, limit=Limit(selected="amount")) plan_mock_2 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=2, limit=Limit(selected="amount")) - ct.data.chargemode.scheduled_charging.plans = {"0": plan_mock_0, "1": plan_mock_1, "2": plan_mock_2} + ct.data.chargemode.scheduled_charging.plans = [plan_mock_0, plan_mock_1, plan_mock_2] # execution selected_plan = ct.scheduled_charging_recent_plan( @@ -230,7 +230,7 @@ def test_scheduled_charging_recent_plan_fulfilled(end_time_mock, expected_plan_n plan_mock_0 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=0, limit=Limit(selected="amount")) plan_mock_1 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=1, limit=Limit(selected="amount")) plan_mock_2 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=2, limit=Limit(selected="amount")) - ct.data.chargemode.scheduled_charging.plans = {"0": plan_mock_0, "1": plan_mock_1, "2": plan_mock_2} + ct.data.chargemode.scheduled_charging.plans = [plan_mock_0, plan_mock_1, plan_mock_2] # execution selected_plan = ct.scheduled_charging_recent_plan( @@ -284,7 +284,7 @@ def test_scheduled_charging_calc_current(plan_data: SelectedPlan, plan = ScheduledChargingPlan(active=True, id=0) plan.limit.selected = selected # json verwandelt Keys in strings - ct.data.chargemode.scheduled_charging.plans = {"0": plan} + ct.data.chargemode.scheduled_charging.plans = [plan] if plan_data: plan_data.plan = plan @@ -318,9 +318,9 @@ def test_scheduled_charging_calc_current_electricity_tariff(loading_hour, expect # setup ct = ChargeTemplate() plan = ScheduledChargingPlan(active=True) + plan.et_active = True plan.limit.selected = "soc" - ct.data.chargemode.scheduled_charging.plans = {"0": plan} - ct.data.chargemode.scheduled_charging.plans["0"].et_active = True + ct.data.chargemode.scheduled_charging.plans = [plan] # für Github-Test keinen Zeitstempel verwenden mock_et_get_loading_hours = Mock(return_value=[datetime.datetime( year=2022, month=5, day=16, hour=8, minute=0).timestamp()]) diff --git a/packages/dataclass_utils/_dataclass_from_dict.py b/packages/dataclass_utils/_dataclass_from_dict.py index 069431311c..6980d761c7 100644 --- a/packages/dataclass_utils/_dataclass_from_dict.py +++ b/packages/dataclass_utils/_dataclass_from_dict.py @@ -2,7 +2,7 @@ import inspect from inspect import FullArgSpec, isclass import typing -from typing import TypeVar, Type, Union, get_origin +from typing import TypeVar, Type, Union, get_args, get_origin T = TypeVar('T') @@ -46,15 +46,20 @@ def _get_argument_value(arg_spec: FullArgSpec, index: int, parameters: dict): def _dataclass_from_dict_recurse(value, requested_type: Type[T]): + if get_origin(requested_type) == list: + # Extrahiere den generischen Typ der Liste + if get_args(requested_type): + generic_type = get_args(requested_type)[0] + # Konvertiere jedes Element der Liste in den generischen Typ + return [_dataclass_from_dict_recurse(item, generic_type) for item in value] + if isinstance(value, dict) and not ( _is_optional_of_dict(requested_type) or 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 + if isinstance(requested_type, type) and issubclass(requested_type, Enum): + return requested_type(value) + return value def _is_optional_of_dict(requested_type): diff --git a/packages/helpermodules/abstract_plans.py b/packages/helpermodules/abstract_plans.py index 99271cf6dd..a2dbc4a4fb 100644 --- a/packages/helpermodules/abstract_plans.py +++ b/packages/helpermodules/abstract_plans.py @@ -3,8 +3,12 @@ from typing import List, Optional -def once_factory() -> List: - return [datetime.datetime.today().strftime("%Y%m%d"), datetime.datetime.today().strftime("%Y%m%d")] +def once_period_factory() -> List: + return [datetime.datetime.today().strftime("%Y-%m-%d"), datetime.datetime.today().strftime("%Y-%m-%d")] + + +def once_date_factory() -> List: + return datetime.datetime.today().strftime("%Y-%m-%d") def weekly_factory() -> List: @@ -27,14 +31,25 @@ def limit_factory() -> Limit: @dataclass -class Frequency: +class FrequencyPeriod: + selected: str = "daily" + once: List[str] = field(default_factory=once_period_factory) + weekly: List[bool] = field(default_factory=weekly_factory) + + +def frequency_period_factory() -> FrequencyPeriod: + return FrequencyPeriod() + + +@dataclass +class FrequencyDate: selected: str = "daily" - once: List[str] = field(default_factory=once_factory) + once: str = field(default_factory=once_date_factory) weekly: List[bool] = field(default_factory=weekly_factory) -def frequency_factory() -> Frequency: - return Frequency() +def frequency_date_factory() -> FrequencyDate: + return FrequencyDate() @dataclass @@ -52,12 +67,12 @@ def scheduled_limit_factory() -> ScheduledLimit: @dataclass class PlanBase: active: bool = True - frequency: Frequency = field(default_factory=frequency_factory) @dataclass class TimeframePlan(PlanBase): time: List[str] = field(default_factory=time_factory) # ToDo: aktuelle Zeit verwenden + 1 Stunde + frequency: FrequencyPeriod = field(default_factory=frequency_period_factory) @dataclass @@ -65,6 +80,7 @@ class ScheduledChargingPlan(PlanBase): current: int = 14 dc_current: float = 145 et_active: bool = False + frequency: FrequencyDate = field(default_factory=frequency_date_factory) id: Optional[int] = None name: str = "neuer Zielladen-Plan" limit: ScheduledLimit = field(default_factory=scheduled_limit_factory) @@ -85,4 +101,5 @@ class TimeChargingPlan(TimeframePlan): @dataclass class AutolockPlan(TimeframePlan): + id: Optional[int] = None name: str = "neuer Plan für Sperren nach Uhrzeit" diff --git a/packages/helpermodules/command.py b/packages/helpermodules/command.py index 45609d3c35..8e25f0a277 100644 --- a/packages/helpermodules/command.py +++ b/packages/helpermodules/command.py @@ -1,3 +1,4 @@ +import copy from dataclasses import asdict import importlib import json @@ -5,7 +6,7 @@ import subprocess from threading import Event import time -from typing import Dict, List, Optional +from typing import Dict, Optional import re import traceback from pathlib import Path @@ -13,12 +14,12 @@ import paho.mqtt.client as mqtt from control.chargelog import chargelog from control.chargepoint import chargepoint -from control.chargepoint.chargepoint_template import get_autolock_plan_default, get_chargepoint_template_default +from control.chargepoint.chargepoint_template import get_chargepoint_template_default from control.ev.charge_template import get_new_charge_template from control.ev.ev_template import EvTemplateData from helpermodules import pub -from helpermodules.abstract_plans import ScheduledChargingPlan, TimeChargingPlan +from helpermodules.abstract_plans import AutolockPlan, ScheduledChargingPlan, TimeChargingPlan from helpermodules.utils.run_command import run_command # ToDo: move to module commands if implemented from modules.backup_clouds.onedrive.api import generateMSALAuthCode, retrieveMSALTokens @@ -30,7 +31,7 @@ from helpermodules.create_debug import create_debug_log from helpermodules.pub import Pub, pub_single from helpermodules.subdata import SubData -from helpermodules.utils.topic_parser import decode_payload +from helpermodules.utils.topic_parser import decode_payload, get_index from control import bat, bridge, data, counter, counter_all, pv from control.ev import ev from modules.chargepoints.internal_openwb.chargepoint_module import ChargepointModule @@ -46,21 +47,23 @@ class Command: """ """ - # Tuple: (Name der maximalen ID, Topic zur Ermittlung, Default-Wert) - MAX_IDS = [("autolock_plan", "chargepoint/template/+/autolock", -1), - ("mqtt_bridge", "system/mqtt/bridge", -1), - ("charge_template", "vehicle/template/charge_template", 0), - ("charge_template_scheduled_plan", - "vehicle/template/charge_template/+/chargemode/scheduled_charging/plans", - -1), - ("charge_template_time_charging_plan", "vehicle/template/charge_template/+/time_charging/plans", -1), - ("chargepoint_template", "chargepoint/template", 0), - ("device", "system/device", -1), - ("ev_template", "vehicle/template/ev_template", 0), - ("vehicle", "vehicle", 0), - ("io_action", "io/action", -1), - ("io_device", "system/io", -1), - ] + # Tuple: (Name der maximalen ID, Regex-Topic zur Ermittlung, Default-Wert) + MAX_IDS = { + "nested payload": + [("autolock_plan", "openWB/chargepoint/template/[0-9]+$", -1), + ("charge_template_scheduled_plan", "openWB/vehicle/template/charge_template/[0-9]+$", -1), + ("charge_template_time_charging_plan", "openWB/vehicle/template/charge_template/[0-9]+$", -1)], + "topic": + [("mqtt_bridge", "openWB/system/mqtt/bridge", -1), + ("vehicle", "openWB/vehicle/[0-9]+/name$", 0)], + "payload": + [("charge_template", "openWB/vehicle/template/charge_template/[0-9]+$", 0), + ("chargepoint_template", "openWB/chargepoint/template/[0-9]+$", 0), + ("device", "openWB/system/device/[0-9]+/config$", -1), + ("ev_template", "openWB/vehicle/template/ev_template/[0-9]+$", 0), + ("io_action", "openWB/io/action/[0-9]+/config$", -1), + ("io_device", "openWB/system/io/[0-9]+/config$", -1)], + } def __init__(self, event_command_completed: Event): try: @@ -74,18 +77,33 @@ def _get_max_ids(self) -> None: """ ermittelt die maximale ID vom Broker """ try: received_topics = ProcessBrokerBranch("").get_max_id() - for id_topic, topic_str, default in self.MAX_IDS: + for id_topic, topic_str, default in self.MAX_IDS["nested payload"]: max_id = default - for topic in received_topics: - search_str = "openWB/" + topic_str.replace("+", "[0-9]+") - result = re.search('^('+search_str+'/*).*$', topic) - if result is not None: - topic_found = result.group(1) - topic_rest = topic.replace(topic_found, "") - current_id_regex = re.search('^([0-9]+)/*.*$', topic_rest) - if current_id_regex is not None: - current_id = int(current_id_regex.group(1)) - max_id = max(current_id, max_id) + for topic, payload in received_topics.items(): + if re.search(topic_str, topic) is not None: + if id_topic == "autolock_plan": + for plan in payload["autolock"]["plans"]: + max_id = max(plan["id"], max_id) + elif id_topic == "charge_template_scheduled_plan": + for plan in payload["chargemode"]["scheduled_charging"]["plans"]: + max_id = max(plan["id"], max_id) + elif id_topic == "charge_template_time_charging_plan": + for plan in payload["time_charging"]["plans"]: + max_id = max(plan["id"], max_id) + setattr(self, f'max_id_{id_topic}', max_id) + Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) + for id_topic, topic_str, default in self.MAX_IDS["topic"]: + max_id = default + for topic in received_topics.keys(): + if re.search(topic_str, topic) is not None: + max_id = max(int(get_index(topic)), max_id) + setattr(self, f'max_id_{id_topic}', max_id) + Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) + for id_topic, topic_str, default in self.MAX_IDS["payload"]: + max_id = default + for topic, payload in received_topics.items(): + if re.search(topic_str, topic) is not None: + max_id = max(payload["id"], max_id) setattr(self, f'max_id_{id_topic}', max_id) Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) except Exception: @@ -389,14 +407,19 @@ def addAutolockPlan(self, connection_id: str, payload: dict) -> None: """ # check if "payload" contains "data.copy" if "data" in payload and "copy" in payload["data"]: - new_autolock_plan = asdict(data.data.cp_template_data[f'cpt{payload["data"]["template"]}'] - .data.autolock.plans[f'{payload["data"]["copy"]}']).copy() - new_autolock_plan["name"] = f'Kopie von {new_autolock_plan["name"]}' + for plan in data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans: + if plan.id == payload["data"]["copy"]: + new_autolock_plan = copy.deepcopy(plan) + break + new_autolock_plan.name = f'Kopie von {new_autolock_plan.name}' else: - new_autolock_plan = get_autolock_plan_default() + new_autolock_plan = AutolockPlan() new_id = self.max_id_autolock_plan + 1 - Pub().pub(f'openWB/set/chargepoint/template/{payload["data"]["template"]}/autolock/{new_id}', - new_autolock_plan) + new_autolock_plan.id = new_id + data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans.append( + new_autolock_plan) + Pub().pub(f'openWB/set/chargepoint/template/{payload["data"]["template"]}', + asdict(data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data)) self.max_id_autolock_plan = new_id Pub().pub("openWB/set/command/max_id/autolock_plan", new_id) pub_user_message( @@ -413,10 +436,13 @@ def removeAutolockPlan(self, connection_id: str, payload: dict) -> None: payload, connection_id, f'Die ID \'{payload["data"]["plan"]}\' ist größer als die ' f'maximal vergebene ID \'{self.max_id_autolock_plan}\'.', MessageType.ERROR) + for plan in data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans: + if plan.id == payload["data"]["plan"]: + data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans.remove(plan) + break Pub().pub( - f'openWB/chargepoint/template/{payload["data"]["template"]}' - f'/autolock/{payload["data"]["plan"]}', - "") + f'openWB/chargepoint/template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data)) pub_user_message( payload, connection_id, f'Plan für Sperren nach Uhrzeit mit ID \'{payload["data"]["plan"]}\' vom Profil ' @@ -485,19 +511,22 @@ def addChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> No """ # check if "payload" contains "data.copy" if "data" in payload and "copy" in payload["data"]: - new_charge_template_schedule_plan = ( - data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'] - .data.chargemode.scheduled_charging.plans[f'{payload["data"]["copy"]}'] - ) + for plan in data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans: + if plan.id == payload["data"]["copy"]: + new_charge_template_schedule_plan = copy.deepcopy(plan) + break new_charge_template_schedule_plan.name = f'Kopie von {new_charge_template_schedule_plan.name}' else: new_charge_template_schedule_plan = ScheduledChargingPlan() new_id = self.max_id_charge_template_scheduled_plan + 1 new_charge_template_schedule_plan.id = new_id + data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans.append( + new_charge_template_schedule_plan) Pub().pub( - f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/chargemode/scheduled_charging/plans/{new_id}', - dataclass_utils.asdict(new_charge_template_schedule_plan)) + f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) self.max_id_charge_template_scheduled_plan = new_id Pub().pub( "openWB/set/command/max_id/charge_template_scheduled_plan", new_id) @@ -515,10 +544,16 @@ def removeChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> payload, connection_id, f'Die ID \'{payload["data"]["plan"]}\' ist größer als die maximal vergebene ' f'ID \'{self.max_id_charge_template_scheduled_plan}\'.', MessageType.ERROR) + for plan in data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans: + if plan.id == payload["data"]["plan"]: + data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans.remove( + plan) + break Pub().pub( - f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/chargemode/scheduled_charging/plans/{payload["data"]["plan"]}', - "") + f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) pub_user_message( payload, connection_id, f'Zielladen-Plan mit ID \'{payload["data"]["plan"]}\' von Profil ' @@ -530,17 +565,20 @@ def addChargeTemplateTimeChargingPlan(self, connection_id: str, payload: dict) - """ # check if "payload" contains "data.copy" if "data" in payload and "copy" in payload["data"]: - new_time_charging_plan = (data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'] - .data.time_charging.plans[f'{payload["data"]["copy"]}']) + for plan in data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans: + if plan.id == payload["data"]["copy"]: + new_time_charging_plan = copy.deepcopy(plan) + break new_time_charging_plan.name = f'Kopie von {new_time_charging_plan.name}' else: new_time_charging_plan = TimeChargingPlan() new_id = self.max_id_charge_template_time_charging_plan + 1 new_time_charging_plan.id = new_id + data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans.append( + new_time_charging_plan) Pub().pub( - f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/time_charging/plans/{new_id}', - dataclass_utils.asdict(new_time_charging_plan)) + f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) self.max_id_charge_template_time_charging_plan = new_id Pub().pub( "openWB/set/command/max_id/charge_template_time_charging_plan", new_id) @@ -555,10 +593,14 @@ def removeChargeTemplateTimeChargingPlan(self, connection_id: str, payload: dict if self.max_id_charge_template_time_charging_plan < payload["data"]["plan"]: log.error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) + for plan in data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans: + if plan.id == payload["data"]["plan"]: + data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans.remove( + plan) + break Pub().pub( - f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/time_charging/plans/{payload["data"]["plan"]}', - "") + f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) pub_user_message( payload, connection_id, f'Zeitladen-Plan mit ID \'{payload["data"]["plan"]}\' zu Profil ' @@ -967,14 +1009,14 @@ def remove_topics(self): """ BrokerClient("processBrokerBranch", self.on_connect, self.__on_message_rm).start_finite_loop() - def get_max_id(self) -> List[str]: + def get_max_id(self) -> Dict[str, str]: try: - self.received_topics = [] + self.received_topics = {} BrokerClient("processBrokerBranch", self.on_connect, self.__on_message_max_id).start_finite_loop() return self.received_topics except Exception: log.exception("Fehler im Command-Modul") - return [] + return {} def check_mqtt_bridge_exists(self, name: str) -> bool: try: @@ -1037,7 +1079,7 @@ def __on_message_rm(self, client, userdata, msg): def __on_message_max_id(self, client, userdata, msg): try: - self.received_topics.append(msg.topic) + self.received_topics.update({msg.topic: decode_payload(msg.payload)}) except Exception: log.exception("Fehler in ProcessBrokerBranch") diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index 30da426397..f07b62b436 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -21,7 +21,6 @@ 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 from helpermodules.broker import BrokerClient from helpermodules.messaging import MessageType, pub_system_message from helpermodules.utils import ProcessingCounter @@ -348,10 +347,9 @@ def process_vehicle_charge_template_topic(self, var: Dict[str, ChargeTemplate], if decode_payload(msg.payload) == "": if "ct"+index in var: var.pop("ct"+index) - if "ct"+index not in var: - var["ct"+index] = ChargeTemplate() - self.process_charge_template_topic(var["ct"+index], msg) - if re.search("/vehicle/template/charge_template/[0-9]+", msg.topic) is not None: + if "ct"+index not in var: + var["ct"+index] = ChargeTemplate() + var["ct"+index].data = dataclass_from_dict(ChargeTemplateData, decode_payload(msg.payload)) # Temporäres ChargeTemplate aktualisieren, wenn persistentes geändert wird for vehicle in self.ev_data.values(): if vehicle.data.charge_template == int(index): @@ -363,62 +361,9 @@ def process_vehicle_charge_template_topic(self, var: Dict[str, ChargeTemplate], # mehrfach gepbulished werden, muss das publishen der temporären Topics 1:1 erfolgen. if re.search("/vehicle/template/charge_template/[0-9]+$", msg.topic) is not None: if decode_payload(msg.payload) == "": - Pub().pub(f"openWB/chargepoint/{cp.chargepoint.num}/charge_template", "") + Pub().pub(f"openWB/chargepoint/{cp.chargepoint.num}/set/charge_template", "") else: - cp.chargepoint.update_bare_charge_template(var["ct"+index]) - elif re.search("/vehicle/template/charge_template/[0-9]+/chargemode/scheduled_charging/" - "plans/[0-9]+", msg.topic) is not None: - plan_id = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - Pub().pub(f"openWB/chargepoint/{cp.chargepoint.num}/set/charge_template/" - f"chargemode/scheduled_charging/plans/{plan_id}", "") - else: - cp.chargepoint.update_charge_template_scheduled_plan( - var["ct"+index].data.chargemode.scheduled_charging.plans[plan_id]) - elif re.search("/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+", - msg.topic) is not None: - plan_id = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - Pub().pub( - f"openWB/chargepoint/{cp.chargepoint.num}/set/charge_template/" - f"time_charging/plans/{plan_id}", "") - else: - cp.chargepoint.update_charge_template_time_plan( - var["ct"+index].data.time_charging.plans[plan_id]) - except Exception: - log.exception("Fehler im subdata-Modul") - - def process_charge_template_topic(self, var: ChargeTemplate, msg: mqtt.MQTTMessage): - try: - if re.search("/chargemode/scheduled_charging/plans/[0-9]+$", msg.topic) is not None: - index_second = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - try: - var.data.chargemode.scheduled_charging.plans.pop(index_second) - except KeyError: - log.error(f"Es konnte kein Zielladen-Plan mit der ID {index_second} " - "in dem Lade-Profil gefunden werden.") - else: - var.data.chargemode.scheduled_charging.plans[ - index_second] = dataclass_from_dict(ScheduledChargingPlan, decode_payload(msg.payload)) - elif re.search("/time_charging/plans/[0-9]+$", msg.topic) is not None: - index_second = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - try: - var.data.time_charging.plans.pop(index_second) - except KeyError: - log.error("Es konnte kein Zeitladen-Plan mit der ID " + - str(index_second)+" in dem Lade-Profil gefunden werden.") - else: - var.data.time_charging.plans[ - index_second] = dataclass_from_dict(TimeChargingPlan, decode_payload(msg.payload)) - else: - # Pläne unverändert übernehmen - scheduled_charging_plans = var.data.chargemode.scheduled_charging.plans - time_charging_plans = var.data.time_charging.plans - var.data = dataclass_from_dict(ChargeTemplateData, decode_payload(msg.payload)) - var.data.time_charging.plans = time_charging_plans - var.data.chargemode.scheduled_charging.plans = scheduled_charging_plans + cp.chargepoint.update_charge_template(var["ct"+index]) except Exception: log.exception("Fehler im subdata-Modul") @@ -480,12 +425,12 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg if re.search("/chargepoint/[0-9]+/set/log$", msg.topic) is not None: var["cp"+index].chargepoint.data.set.log = dataclass_from_dict( Log, decode_payload(msg.payload)) + elif "charge_template" in msg.topic: + var["cp"+index].chargepoint.data.set.charge_template = ChargeTemplate() + var["cp"+index].chargepoint.data.set.charge_template.data = dataclass_from_dict( + ChargeTemplateData, decode_payload(msg.payload)) else: - if "charge_template" in msg.topic: - self.process_charge_template_topic( - var["cp"+index].chargepoint.data.set.charge_template, msg) - else: - self.set_json_payload_class(var["cp"+index].chargepoint.data.set, msg) + self.set_json_payload_class(var["cp"+index].chargepoint.data.set, msg) elif re.search("/chargepoint/[0-9]+/get/", msg.topic) is not None: if re.search("/chargepoint/[0-9]+/get/connected_vehicle/", msg.topic) is not None: self.set_json_payload_class(var["cp"+index].chargepoint.data.get.connected_vehicle, msg) @@ -553,22 +498,12 @@ def process_chargepoint_template_topic(self, var: Dict[str, chargepoint.CpTempla try: index = get_index(msg.topic) payload = decode_payload(msg.payload) - if re.search("/chargepoint/template/[0-9]+/autolock/", msg.topic) is not None: - index_second = get_second_index(msg.topic) - if payload == "": - var["cpt"+index].data.autolock.plans.pop(index_second) - else: - var["cpt"+index].data.autolock.plans[ - index_second] = dataclass_from_dict(AutolockPlan, payload) + if payload == "": + var.pop("cpt"+index) else: - if payload == "": - var.pop("cpt"+index) - else: - if "cpt"+index not in var: - var["cpt"+index] = CpTemplate() - autolock_plans = var["cpt"+index].data.autolock.plans - var["cpt"+index].data = dataclass_from_dict(CpTemplateData, payload) - var["cpt"+index].data.autolock.plans = autolock_plans + if "cpt"+index not in var: + var["cpt"+index] = CpTemplate() + var["cpt"+index].data = dataclass_from_dict(CpTemplateData, payload) except Exception: log.exception("Fehler im subdata-Modul") diff --git a/packages/helpermodules/timecheck.py b/packages/helpermodules/timecheck.py index 6568379b59..b144d45b3d 100644 --- a/packages/helpermodules/timecheck.py +++ b/packages/helpermodules/timecheck.py @@ -2,7 +2,7 @@ """ import logging import datetime -from typing import Dict, List, Optional, Tuple, TypeVar, Union +from typing import List, Optional, Tuple, TypeVar, Union from helpermodules.utils.error_handling import ImportErrorContext with ImportErrorContext(): @@ -46,12 +46,12 @@ def is_now_in_locking_time(now: datetime.datetime, T = TypeVar("T", AutolockPlan, TimeChargingPlan) -def check_plans_timeframe(plans: Dict[int, T]) -> Optional[T]: +def check_plans_timeframe(plans: List[T]) -> Optional[T]: """ gibt den ersten aktiven Plan zurück. None, falls kein Plan aktiv ist. """ state = False try: - for plan in plans.values(): + for plan in plans: if plan.active: state = check_timeframe(plan) if state: diff --git a/packages/helpermodules/timecheck_test.py b/packages/helpermodules/timecheck_test.py index 72d72c5e34..e22afce0ac 100644 --- a/packages/helpermodules/timecheck_test.py +++ b/packages/helpermodules/timecheck_test.py @@ -4,7 +4,8 @@ import pytest from helpermodules import timecheck -from helpermodules.abstract_plans import AutolockPlan, Frequency, ScheduledChargingPlan, TimeChargingPlan +from helpermodules.abstract_plans import (AutolockPlan, FrequencyDate, FrequencyPeriod, ScheduledChargingPlan, + TimeChargingPlan) class Params: @@ -43,7 +44,7 @@ def test_check_end_time(time: str, plan_fulfilled: bool, expected_remaining_time: float): # setup - plan = Mock(spec=ScheduledChargingPlan, time=time, frequency=Mock(spec=Frequency, selected=selected,)) + plan = Mock(spec=ScheduledChargingPlan, time=time, frequency=Mock(spec=FrequencyDate, selected=selected,)) if date: setattr(plan.frequency, selected, date) @@ -74,42 +75,42 @@ def test_get_next_charging_day(weekday: int, weekly: List[bool], expected_days: @pytest.mark.parametrize( "plan, now, expected_state", [pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="once", once=["2022-05-16", "2022-05-16"])), + frequency=FrequencyPeriod(selected="once", once=["2022-05-16", "2022-05-16"])), "2022-05-16 9:50", False, id="once, 10-12 at 9.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="once", once=["2022-05-16", "2022-05-16"])), + frequency=FrequencyPeriod(selected="once", once=["2022-05-16", "2022-05-16"])), "2022-05-16 10:50", True, id="once, 10-12 at 10.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="once", once=["2022-05-16", "2022-05-16"])), + frequency=FrequencyPeriod(selected="once", once=["2022-05-16", "2022-05-16"])), "2022-05-16 13:50", False, id="once, 10-12 at 13.50"), - pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-16 11:50", True, id="daily, 10-12 at 11.50"), - pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-16 22:50", True, id="daily, 22-02 at 22.50"), - pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-17 1:50", True, id="daily, 22-02 at 1.50"), - pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-17 2:50", False, id="daily, 22-02 at 2.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-16 10:50", True, id="weekly, 10-12 at 10.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="weekly", weekly=[0, 1, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[0, 1, 0, 0, 0, 0, 0])), "2022-05-16 10:50", False, id="weekly, 10-12 at 10.50"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-16 22:50", True, id="weekly, 22-02 at 22.50"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-17 1:50", True, id="weekly, 22-02 at 1.50"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-16 1:50", False, id="weekly, 22-02 at 1.50, weekday before"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-18 1:50", False, id="weekly, 22-02 at 1.50, weekday after"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-17 2:50", False, id="weekly, 22-02 at 2.50"), ] ) diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index a55883a1d8..72a92f9461 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -56,7 +56,7 @@ class UpdateConfig: - DATASTORE_VERSION = 84 + DATASTORE_VERSION = 85 valid_topic = [ "^openWB/bat/config/configured$", @@ -89,7 +89,7 @@ class UpdateConfig: "^openWB/chargepoint/get/daily_exported$", "^openWB/chargepoint/get/daily_imported$", "^openWB/chargepoint/template/[0-9]+$", - "^openWB/chargepoint/template/[0-9]+/autolock/[0-9]+$", + "^openWB/chargepoint/template/[0-9]+/autolock/[0-9]+$", # OBSOLET seit 2.1.8 "^openWB/chargepoint/[0-9]+/config$", "^openWB/chargepoint/[0-9]+/control_parameter/chargemode$", "^openWB/chargepoint/[0-9]+/control_parameter/current_plan$", @@ -141,8 +141,6 @@ class UpdateConfig: "^openWB/chargepoint/[0-9]+/get/rfid$", "^openWB/chargepoint/[0-9]+/get/rfid_timestamp$", "^openWB/chargepoint/[0-9]+/set/charging_ev$", - "^openWB/chargepoint/[0-9]+/set/charge_template/time_charging/plans/[0-9]+$", - "^openWB/chargepoint/[0-9]+/set/charge_template/chargemode/scheduled_charging/plans/[0-9]+$", "^openWB/chargepoint/[0-9]+/set/charge_template$", "^openWB/chargepoint/[0-9]+/set/current$", "^openWB/chargepoint/[0-9]+/set/energy_to_charge$", @@ -350,7 +348,8 @@ class UpdateConfig: "^openWB/vehicle/set/vehicle_update_completed$", "^openWB/vehicle/template/ev_template/[0-9]+$", - "^openWB/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+$", + "^openWB/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+$", # OBSOLET seit 2.1.8 + # OBSOLET seit 2.1.8 "^openWB/vehicle/template/charge_template/[0-9]+/chargemode/scheduled_charging/plans/[0-9]+$", "^openWB/vehicle/template/charge_template/[0-9]+", "^openWB/vehicle/[0-9]+/charge_template$", @@ -2253,3 +2252,58 @@ def upgrade(topic: str, payload) -> None: return {component_topic: config_payload} self._loop_all_received_topics(upgrade) self.__update_topic("openWB/system/datastore_version", 84) + + def upgrade_datastore_84(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("^openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None: + index = get_index(topic) + template = decode_payload(payload) + if template.get("id") is None: + template["id"] = int(index) + if template["chargemode"]["scheduled_charging"].get("plans") is None: + template["chargemode"]["scheduled_charging"]["plans"] = [] + if template["time_charging"].get("plans") is None: + template["time_charging"]["plans"] = [] + for template_topic, template_payload in self.all_received_topics.items(): + if re.search(f"openWB/vehicle/template/charge_template/{index}/chargemode/scheduled_charging/plans/" + "[0-9]+$", template_topic) is not None: + plan = decode_payload(template_payload) + if isinstance(plan["frequency"]["once"], List): + plan["frequency"]["once"] = plan["frequency"]["once"][0] + template["chargemode"]["scheduled_charging"]["plans"].append(plan) + elif re.search(f"openWB/vehicle/template/charge_template/{index}/time_charging/plans/" + "[0-9]+$", template_topic) is not None: + plan = decode_payload(template_payload) + template["time_charging"]["plans"].append(plan) + return {topic: template} + elif re.search("^openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None: + template = decode_payload(payload) + if template.get("id") is None: + template["id"] = int(get_index(topic)) + return {topic: template} + elif re.search("openWB/chargepoint/template/[0-9]+$", topic) is not None: + index = get_index(topic) + template = decode_payload(payload) + if template["autolock"].get("plans") is None: + template["autolock"]["plans"] = [] + if template.get("id") is None: + template["id"] = int(index) + for template_topic, template_payload in self.all_received_topics.items(): + if re.search("^openWB/chargepoint/template/[0-9]+/autolock/[0-9]+$", template_topic) is not None: + plan = decode_payload(template_payload) + if plan.get("id") is None: + plan["id"] = int(get_second_index(template_topic)) + template["autolock"]["plans"].append(plan) + return {topic: template} + + def cp_upgrade(topic: str, payload) -> Optional[dict]: + if re.search("openWB/chargepoint/[0-9]+/config", topic) is not None: + payload = decode_payload(payload) + index = get_index(topic) + if payload["template"] is not None: + charge_template = decode_payload( + self.all_received_topics[f'openWB/vehicle/template/charge_template/{payload["template"]}']) + return {f'openWB/chargepoint/{index}/set/charge_template': charge_template} + self._loop_all_received_topics(upgrade) + self._loop_all_received_topics(cp_upgrade) + self.__update_topic("openWB/system/datastore_version", 85) diff --git a/packages/modules/display_themes/cards/source/src/stores/mqtt.js b/packages/modules/display_themes/cards/source/src/stores/mqtt.js index 562b023f6a..f53d0eb91f 100644 --- a/packages/modules/display_themes/cards/source/src/stores/mqtt.js +++ b/packages/modules/display_themes/cards/source/src/stores/mqtt.js @@ -835,16 +835,24 @@ export const useMqttStore = defineStore("mqtt", { }, getChargePointConnectedVehicleScheduledChargingPlans(state) { return (chargePointId) => { - return state.getWildcardTopics( - `openWB/chargepoint/${chargePointId}/set/charge_template/chargemode/scheduled_charging/plans/+`, - ); + if (state.getChargePointConnectedVehicleChargeTemplate(chargePointId)) { + return state.getChargePointConnectedVehicleChargeTemplate( + chargePointId, + ).chargemode.scheduled_charging.plans; + } + return {}; }; }, getChargePointConnectedVehicleTimeChargingPlans(state) { return (chargePointId) => { - return state.getWildcardTopics( - `openWB/chargepoint/${chargePointId}/set/charge_template/time_charging/plans/+`, - ); + if ( + state.getChargePointConnectedVehicleChargeTemplate(chargePointId) + ) { + return state.getChargePointConnectedVehicleChargeTemplate( + chargePointId, + ).time_charging.plans; + } + return {}; }; }, diff --git a/packages/modules/display_themes/cards/source/src/views/ChargePointsView.vue b/packages/modules/display_themes/cards/source/src/views/ChargePointsView.vue index ebbe1c61b4..561e5d9a4f 100644 --- a/packages/modules/display_themes/cards/source/src/views/ChargePointsView.vue +++ b/packages/modules/display_themes/cards/source/src/views/ChargePointsView.vue @@ -338,23 +338,34 @@ export default { } }, setChargePointConnectedVehicleScheduledChargingPlanActive( - plan_key, + id, + plan_id, active, ) { - const plan = this.mqttStore.updateState( - `${plan_key}`, + console.log(`setChargePointConnectedVehicleScheduledChargingPlanActive: ${id} ${plan_id} - ${active}`); + const templateTopic = `openWB/chargepoint/${id}/set/charge_template`; + const template = this.mqttStore.updateState( + templateTopic, active, - "active", + `chargemode.scheduled_charging.plans.${plan_id}.active`, + ); + this.$root.sendTopicToBroker( + templateTopic, + template, ); - this.$root.sendTopicToBroker(`${plan_key}`, plan); }, - setChargePointConnectedVehicleTimeChargingPlanActive(plan_key, active) { - const plan = this.mqttStore.updateState( - `${plan_key}`, + setChargePointConnectedVehicleTimeChargingPlanActive(id, plan_id, active) { + console.log(`setChargePointConnectedVehicleTimeChargingPlanActive: ${id} ${plan_id} - ${active}`); + const templateTopic = `openWB/chargepoint/${id}/set/charge_template`; + const template = this.mqttStore.updateState( + templateTopic, active, - "active", + `time_charging.plans.${plan_id}.active`, + ) + this.$root.sendTopicToBroker( + templateTopic, + template, ); - this.$root.sendTopicToBroker(`${plan_key}`, plan); }, }, }; @@ -1254,11 +1265,12 @@ export default { > @@ -1287,7 +1299,8 @@ export default { :color="plan.active ? 'success' : 'danger'" @click=" setChargePointConnectedVehicleScheduledChargingPlanActive( - planKey, + modalChargePointId, + plan.id, !plan.active, ) " @@ -1388,11 +1401,12 @@ export default { @@ -1421,7 +1435,8 @@ export default { :color="plan.active ? 'success' : 'danger'" @click=" setChargePointConnectedVehicleTimeChargingPlanActive( - planKey, + modalChargePointId, + plan.id, !plan.active, ) " diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue index 7a87bc5011..0b553129e9 100755 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue @@ -54,6 +54,13 @@ > + + +