From ec3f403c1a68c7114df77b6bb6840ed0e5f9e3ce Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 18 Sep 2024 11:56:28 +0200 Subject: [PATCH 1/8] OCPP --- packages/control/chargepoint/chargepoint.py | 20 ++++ .../control/chargepoint/chargepoint_data.py | 2 + packages/control/ocpp.py | 108 ++++++++++++++++++ packages/control/ocpp_test.py | 69 +++++++++++ packages/control/optional.py | 94 +++++---------- packages/control/optional_data.py | 84 ++++++++++++++ packages/helpermodules/setdata.py | 9 +- packages/helpermodules/subdata.py | 2 + packages/helpermodules/update_config.py | 6 + packages/main.py | 2 + requirements.txt | 7 ++ 11 files changed, 336 insertions(+), 67 deletions(-) create mode 100644 packages/control/ocpp.py create mode 100644 packages/control/ocpp_test.py create mode 100644 packages/control/optional_data.py diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index f4998fb97b..c5c8c35728 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -206,6 +206,14 @@ def is_charging_possible(self) -> Tuple[bool, Optional[str]]: def _process_charge_stop(self) -> None: # Charging Ev ist noch das EV des vorherigen Zyklus, wenn das nicht -1 war und jetzt nicht mehr geladen # werden soll (-1), Daten zurücksetzen. + # Ocpp Stop Funktion aufrufen + if not self.data.get.plug_state and self.data.set.ocpp_transaction_id is not None: + data.data.optional_data.stop_transaction( + self.data.config.ocpp_chargebox_id, + self.data.get.imported, + self.data.set.ocpp_transaction_id, + self.data.set.rfid) + Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/ocpp_transaction_id", None) if self.data.set.charging_ev_prev != -1: # Daten zurücksetzen, wenn nicht geladen werden soll. self.reset_control_parameter_at_charge_stop() @@ -712,6 +720,18 @@ def update(self, ev_list: Dict[str, Ev]) -> None: self._pub_connected_vehicle(ev_list[f"ev{vehicle}"]) else: self._pub_configured_ev(ev_list) + # OCPP Start Transaction nach Anstecken + if self.data.get.plug_state and self.data.set.plug_state_prev is False: + try: + self.data.set.ocpp_transaction_id = data.data.optional_data.start_transaction( + self.data.config.ocpp_chargebox_id, + self.num, + self.data.set.rfid, + self.data.get.imported) + Pub().pub("openWB/set/chargepoint/"+str(self.num) + + "/set/ocpp_transaction_id", self.data.set.ocpp_transaction_id) + except Exception: + log.exception("Fehler im OCPP-Modul _start_transaction()") # SoC nach Anstecken aktualisieren if ((self.data.get.plug_state and self.data.set.plug_state_prev is False) or (self.data.get.plug_state is False and self.data.set.plug_state_prev)): diff --git a/packages/control/chargepoint/chargepoint_data.py b/packages/control/chargepoint/chargepoint_data.py index d7807f0eda..a42511116a 100644 --- a/packages/control/chargepoint/chargepoint_data.py +++ b/packages/control/chargepoint/chargepoint_data.py @@ -140,6 +140,7 @@ class Set: rfid: Optional[str] = None target_current: float = 0 # Soll-Strom aus fest vorgegebener Stromstärke charging_ev_data: Ev = field(default_factory=ev_factory) + ocpp_transaction_id: Optional[int] = None @dataclass @@ -154,6 +155,7 @@ class Config: auto_phase_switch_hw: bool = False control_pilot_interruption_hw: bool = False id: int = 0 + ocpp_chargebox_id: Optional[str] = None def __post_init__(self): self.event_update_state: threading.Event diff --git a/packages/control/ocpp.py b/packages/control/ocpp.py new file mode 100644 index 0000000000..66f49e0053 --- /dev/null +++ b/packages/control/ocpp.py @@ -0,0 +1,108 @@ +from datetime import datetime +import json +import logging +from ocpp.v16 import call, ChargePoint as OcppChargepoint +import websockets +import asyncio +from typing import Callable, Optional + +from control import data +from control.optional_data import OptionalProtocol + + +log = logging.getLogger(__name__) + + +class OcppMixin: + def _get_formatted_time(self: OptionalProtocol) -> str: + return datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") + + def _process_call(self: OptionalProtocol, + chargebox_id: str, + func: Callable) -> Optional[websockets.WebSocketClientProtocol]: + async def make_call() -> websockets.WebSocketClientProtocol: + async with websockets.connect(self.data.ocpp.url+chargebox_id, subprotocols=[self.data.ocpp.version]) as ws: + try: + cp = OcppChargepoint(chargebox_id, ws, 2) + await cp.call(func) + except asyncio.exceptions.TimeoutError: + # log.exception("Erwarteter TimeOut StartTransaction") + pass + except websockets.exceptions.InvalidStatusCode: + raise Exception( + f"Chargebox ID {chargebox_id} konnte nicht im OCPP-Central System gefunden werden.") + return ws + if self.data.ocpp.active and chargebox_id: + return asyncio.run(make_call()) + return None + + def boot_notification(self: OptionalProtocol, + chargebox_id: str, + model: str, + serial_number: str) -> Optional[int]: + try: + self._process_call(chargebox_id, call.BootNotification( + charge_point_model=model, + charge_point_vendor="openWB", + firmware_version=data.data.system_data["system"].data["version"], + meter_serial_number=serial_number + )) + except Exception: + log.exception("Fehler OCPP: _start_transaction") + + def start_transaction(self: OptionalProtocol, + chargebox_id: str, + connector_id: int, + id_tag: str, + imported: int) -> Optional[int]: + try: + ws = self._process_call(chargebox_id, call.StartTransaction( + connector_id=connector_id, + id_tag=id_tag if id_tag else "", + meter_start=int(imported), + timestamp=self._get_formatted_time() + )) + if ws: + return json.loads(ws.messages[0])[2]["transactionId"] + except Exception: + log.exception("Fehler OCPP: _start_transaction") + return None + + def transfer_values(self: OptionalProtocol, chargebox_id: str, connector_id: int, imported: int) -> None: + try: + self._process_call(chargebox_id, call.MeterValues( + connector_id=connector_id, + meter_value=[{"timestamp": self._get_formatted_time(), + "sampledValue": [ + { + "value": f'{int(imported)}', + "context": "Sample.Periodic", + "format": "Raw", + "measurand": "Energy.Active.Import.Register", + "unit": "Wh" + }, + ]}], + )) + except Exception: + log.exception("Fehler OCPP: _transfer_values") + + def send_heart_beat(self: OptionalProtocol, chargebox_id: str) -> None: + try: + self._process_call(chargebox_id, call.Heartbeat()) + except Exception: + log.exception("Fehler OCPP: _send_heart_beat") + + def stop_transaction(self: OptionalProtocol, + chargebox_id: str, + imported: int, + transaction_id: int, + id_tag: str) -> None: + try: + self._process_call(chargebox_id, call.StopTransaction(meter_stop=int(imported), + timestamp=self._get_formatted_time(), + transaction_id=transaction_id, + reason="EVDisconnected", + id_tag=id_tag if id_tag else "" + )) + except Exception: + log.exception("Fehler OCPP: _stop_transaction") diff --git a/packages/control/ocpp_test.py b/packages/control/ocpp_test.py new file mode 100644 index 0000000000..d7d1a9fda1 --- /dev/null +++ b/packages/control/ocpp_test.py @@ -0,0 +1,69 @@ +from unittest.mock import Mock +import pytest + +from control import data +from control.chargepoint.chargepoint import Chargepoint +from modules.devices.mqtt.config import Mqtt +from modules.devices.mqtt.device import create_device + + +@pytest.fixture() +def mock_data() -> None: + data.data_init(Mock()) + data.data.optional_data.data.ocpp.active = True + data.data.optional_data.data.ocpp.url = "ws://localhost:9000/" + + +def test_start_transaction(mock_data, monkeypatch): + cp = Chargepoint(1, None) + cp.data.config.ocpp_chargebox_id = "cp1" + cp.data.get.plug_state = True + + start_transaction_mock = Mock() + monkeypatch.setattr(data.data.optional_data, "start_transaction", start_transaction_mock) + _pub_configured_ev_mock = Mock() + monkeypatch.setattr(cp, "_pub_configured_ev", _pub_configured_ev_mock) + prepare_cp_mock = Mock(return_value=(-1, None)) + monkeypatch.setattr(cp, "prepare_cp", prepare_cp_mock) + + cp.update([]) + + assert start_transaction_mock.call_args == (("cp1", 1, None, 0),) + + +def test_stop_transaction(mock_data, monkeypatch): + cp = Chargepoint(1, None) + cp.data.config.ocpp_chargebox_id = "cp1" + cp.data.get.plug_state = False + cp.data.set.ocpp_transaction_id = 124 + cp.data.set.charging_ev_prev = -1 + + stop_transaction_mock = Mock() + monkeypatch.setattr(data.data.optional_data, "stop_transaction", stop_transaction_mock) + + cp._process_charge_stop() + + assert stop_transaction_mock.call_args == (("cp1", 0, 124, None),) + + +def test_send_ocpp_data(mock_data, monkeypatch): + data.data.cp_data["cp1"] = Chargepoint(1, None) + data.data.cp_data["cp1"].data.config.ocpp_chargebox_id = "cp1" + data.data.cp_data["cp1"].data.get.plug_state = True + data.data.cp_data["cp1"].chargepoint_module = create_device(Mqtt()) + data.data.cp_data["cp1"].data.get.serial_number = "123456" + transfer_values_mock = Mock() + monkeypatch.setattr(data.data.optional_data, "transfer_values", transfer_values_mock) + boot_notification_mock = Mock() + monkeypatch.setattr(data.data.optional_data, "boot_notification", boot_notification_mock) + send_heart_beat_mock = Mock() + monkeypatch.setattr(data.data.optional_data, "send_heart_beat", send_heart_beat_mock) + + data.data.optional_data.ocpp_boot_notification_send = False + + data.data.optional_data._transfer_meter_values() + + boot_notification_mock.call_args == (("cp1", "mqtt", "123456"),) + send_heart_beat_mock.call_args == (("cp1",),) + transfer_values_mock.call_args == (("cp1", 1, 0),) + assert data.data.optional_data.ocpp_boot_notification_send is True diff --git a/packages/control/optional.py b/packages/control/optional.py index a4007408d1..5dbd35837b 100644 --- a/packages/control/optional.py +++ b/packages/control/optional.py @@ -1,91 +1,31 @@ """Optionale Module """ -from dataclasses import dataclass, field import logging from math import ceil # Aufrunden import threading -from typing import Dict, List +from typing import List -from dataclass_utils.factories import empty_dict_factory +from control import data +from control.ocpp import OcppMixin +from control.optional_data import OptionalData from helpermodules import hardware_configuration from helpermodules.constants import NO_ERROR from helpermodules.pub import Pub from helpermodules.timecheck import create_unix_timestamp_current_full_hour from helpermodules.utils import thread_handler from modules.common.configurable_tariff import ConfigurableElectricityTariff -from modules.display_themes.cards.config import CardsDisplayTheme log = logging.getLogger(__name__) -@dataclass -class EtGet: - fault_state: int = 0 - fault_str: str = NO_ERROR - prices: Dict = field(default_factory=empty_dict_factory) - - -def get_factory() -> EtGet: - return EtGet() - - -@dataclass -class Et: - get: EtGet = field(default_factory=get_factory) - - -def et_factory() -> Et: - return Et() - - -@dataclass -class InternalDisplay: - active: bool = False - on_if_plugged_in: bool = True - pin_active: bool = False - pin_code: str = "0000" - standby: int = 60 - theme: CardsDisplayTheme = CardsDisplayTheme() - - -def int_display_factory() -> InternalDisplay: - return InternalDisplay() - - -@dataclass -class Led: - active: bool = False - - -def led_factory() -> Led: - return Led() - - -@dataclass -class Rfid: - active: bool = False - - -def rfid_factory() -> Rfid: - return Rfid() - - -@dataclass -class OptionalData: - et: Et = field(default_factory=et_factory) - int_display: InternalDisplay = field(default_factory=int_display_factory) - led: Led = field(default_factory=led_factory) - rfid: Rfid = field(default_factory=rfid_factory) - dc_charging: bool = False - - -class Optional: +class Optional(OcppMixin): def __init__(self): try: self.data = OptionalData() self.et_module: ConfigurableElectricityTariff = None self.data.dc_charging = hardware_configuration.get_hardware_configuration_setting("dc_charging") Pub().pub("openWB/optional/dc_charging", self.data.dc_charging) + self.ocpp_boot_notification_send = False except Exception: log.exception("Fehler im Optional-Modul") @@ -153,3 +93,25 @@ def et_get_prices(self): Pub().pub("openWB/set/optional/et/get/fault_str", NO_ERROR) except Exception: log.exception("Fehler im Optional-Modul") + + def ocpp_transfer_meter_values(self): + try: + if self.data.ocpp.active: + thread_handler(threading.Thread(target=self._transfer_meter_values, args=(), name="OCPP Client")) + except Exception: + log.exception("Fehler im OCPP-Optional-Modul") + + def _transfer_meter_values(self): + for cp in data.data.cp_data.values(): + if self.ocpp_boot_notification_send is False: + # Boot-Notfification nicht in der init-Funktion aufrufen, da noch nicht alles initialisiert ist + self.boot_notification(cp.data.config.ocpp_chargebox_id, + cp.chargepoint_module.config.type, + cp.data.get.serial_number) + self.ocpp_boot_notification_send = True + if cp.data.set.ocpp_transaction_id is not None: + try: + self.send_heart_beat(cp.data.config.ocpp_chargebox_id) + self.transfer_values(cp.data.config.ocpp_chargebox_id, cp.num, int(cp.data.get.imported)) + except Exception: + log.exception("Fehler Trigger Meter Values") diff --git a/packages/control/optional_data.py b/packages/control/optional_data.py new file mode 100644 index 0000000000..39e0a473e2 --- /dev/null +++ b/packages/control/optional_data.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass, field +from typing import Dict, Protocol + +from dataclass_utils.factories import empty_dict_factory +from helpermodules.constants import NO_ERROR +from modules.display_themes.cards.config import CardsDisplayTheme + + +@dataclass +class EtGet: + fault_state: int = 0 + fault_str: str = NO_ERROR + prices: Dict = field(default_factory=empty_dict_factory) + + +def get_factory() -> EtGet: + return EtGet() + + +@dataclass +class Et: + get: EtGet = field(default_factory=get_factory) + + +def et_factory() -> Et: + return Et() + + +@dataclass +class InternalDisplay: + active: bool = False + on_if_plugged_in: bool = True + pin_active: bool = False + pin_code: str = "0000" + standby: int = 60 + theme: CardsDisplayTheme = CardsDisplayTheme() + + +def int_display_factory() -> InternalDisplay: + return InternalDisplay() + + +@dataclass +class Led: + active: bool = False + + +def led_factory() -> Led: + return Led() + + +@dataclass +class Rfid: + active: bool = False + + +def rfid_factory() -> Rfid: + return Rfid() + + +@dataclass +class Ocpp: + active: bool = False + url: str = "" + version: str = "ocpp1.6" + + +def ocpp_factory() -> Ocpp: + return Ocpp() + + +@dataclass +class OptionalData: + et: Et = field(default_factory=et_factory) + int_display: InternalDisplay = field(default_factory=int_display_factory) + led: Led = field(default_factory=led_factory) + rfid: Rfid = field(default_factory=rfid_factory) + dc_charging: bool = False + ocpp: Ocpp = field(default_factory=ocpp_factory) + + +class OptionalProtocol(Protocol): + @property + def data(self) -> OptionalData: ... diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index 1658400283..03c4429728 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -541,6 +541,7 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): elif ("/set/manual_lock" in msg.topic or "/set/perform_control_pilot_interruption" in msg.topic or "/set/perform_phase_switch" in msg.topic or + "/set/ocpp_transaction_active" in msg.topic or "/set/plug_state_prev" in msg.topic): self._validate_value(msg, bool) elif "/set/autolock_state" in msg.topic: @@ -548,6 +549,8 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): elif ("/set/rfid" in msg.topic or "/set/plug_time" in msg.topic): self._validate_value(msg, float) + elif "/set/ocpp_transaction_id" in msg.topic: + self._validate_value(msg, int) elif "/set/log" in msg.topic: self._validate_value(msg, "json") elif "/config/ev" in msg.topic: @@ -853,8 +856,12 @@ def process_optional_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, str) elif "openWB/set/optional/et/provider" in msg.topic: self._validate_value(msg, "json") - elif "openWB/set/optional/rfid/active" in msg.topic: + elif ("openWB/set/optional/rfid/active" in msg.topic or + "openWB/set/optional/ocpp/active" in msg.topic): self._validate_value(msg, bool) + elif ("openWB/set/optional/ocpp/url" in msg.topic or + "openWB/set/optional/ocpp/version" in msg.topic): + self._validate_value(msg, str) elif "openWB/set/optional/int_display/rotation" in msg.topic: self._validate_value(msg, int, [(0, 0), (90, 90), (180, 180), (270, 270)]) elif "openWB/set/optional/int_display/active" in msg.topic: diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index a866fff1b5..3381780a71 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -659,6 +659,8 @@ def process_optional_topic(self, var: optional.Optional, msg: mqtt.MQTTMessage): self.set_json_payload_class(var.data.led, msg) elif re.search("/optional/rfid/", msg.topic) is not None: self.set_json_payload_class(var.data.rfid, msg) + elif re.search("/optional/ocpp/", msg.topic) is not None: + self.set_json_payload_class(var.data.ocpp, msg) elif re.search("/optional/int_display/", msg.topic) is not None: self.set_json_payload_class(var.data.int_display, msg) if re.search("/(standby|active|rotation)$", msg.topic) is not None: diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 6251afc440..aaf6baadd5 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -128,6 +128,8 @@ class UpdateConfig: "^openWB/chargepoint/[0-9]+/set/log$", "^openWB/chargepoint/[0-9]+/set/phases_to_use$", "^openWB/chargepoint/[0-9]+/set/charging_ev_prev$", + "^openWB/chargepoint/[0-9]+/set/ocpp_transaction_id$", + "^openWB/chargepoint/[0-9]+/set/ocpp_transaction_active$", "^openWB/command/max_id/autolock_plan$", "^openWB/command/max_id/charge_template$", @@ -252,6 +254,9 @@ class UpdateConfig: "^openWB/optional/int_display/only_local_charge_points", "^openWB/optional/led/active$", "^openWB/optional/rfid/active$", + "^openWB/optional/ocpp/active$", + "^openWB/optional/ocpp/url$", + "^openWB/optional/ocpp/version$", "^openWB/pv/config/configured$", "^openWB/pv/get/exported$", @@ -499,6 +504,7 @@ class UpdateConfig: ("openWB/optional/int_display/theme", dataclass_utils.asdict(CardsDisplayTheme())), ("openWB/optional/int_display/only_local_charge_points", False), ("openWB/optional/led/active", False), + ("openWB/optional/ocpp/active", False), ("openWB/optional/rfid/active", False), ("openWB/system/backup_cloud/config", NO_MODULE), ("openWB/system/backup_cloud/backup_before_update", True), diff --git a/packages/main.py b/packages/main.py index 8a0bc15694..659d5aafd4 100755 --- a/packages/main.py +++ b/packages/main.py @@ -33,6 +33,7 @@ from control import data from control import process from control.algorithm import algorithm +from control import optional from helpermodules.utils import exit_after from modules import update_soc from modules.internal_chargepoint_handler.internal_chargepoint_handler import GeneralInternalChargepointHandler @@ -94,6 +95,7 @@ def handler5MinAlgorithm(self): update_pv_monthly_yearly_yields() data.data.general_data.grid_protection() data.data.optional_data.et_get_prices() + data.data.optional_data.ocpp_transfer_meter_values() data.data.counter_all_data.validate_hierarchy() except KeyboardInterrupt: log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) diff --git a/requirements.txt b/requirements.txt index 78cb018b81..1fe5751786 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,3 +23,10 @@ pytz==2023.3.post1 grpcio==1.60.1 protobuf==4.25.3 bimmer_connected==0.16.1 +# ocpp +jsonschema==4.22.0 +jsonschema-specifications==2023.12.1 +ocpp==1.0.0 +referencing==0.35.1 +rpds-py==0.18.1 +websockets==12.0 From c8e76e0b6f5346acfebb2bd5f6f072cb3a2de8c6 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Tue, 1 Oct 2024 09:11:46 +0200 Subject: [PATCH 2/8] fixes --- packages/control/chargepoint/chargepoint.py | 23 +++--- packages/control/ocpp.py | 76 ++++++++++++------- packages/control/optional.py | 26 ++++--- packages/control/process.py | 3 + .../chargepoints/mqtt/chargepoint_module.py | 3 +- 5 files changed, 77 insertions(+), 54 deletions(-) diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index c5c8c35728..66d86b708c 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -32,6 +32,7 @@ from control.ev import Ev from control import phase_switch from control.chargepoint.chargepoint_state import ChargepointState +from helpermodules.constants import NO_ERROR from helpermodules.phase_mapping import convert_single_evu_phase_to_cp_phase from helpermodules.pub import Pub from helpermodules import timecheck @@ -210,6 +211,7 @@ def _process_charge_stop(self) -> None: if not self.data.get.plug_state and self.data.set.ocpp_transaction_id is not None: data.data.optional_data.stop_transaction( self.data.config.ocpp_chargebox_id, + self.chargepoint_module.fault_state, self.data.get.imported, self.data.set.ocpp_transaction_id, self.data.set.rfid) @@ -721,17 +723,16 @@ def update(self, ev_list: Dict[str, Ev]) -> None: else: self._pub_configured_ev(ev_list) # OCPP Start Transaction nach Anstecken - if self.data.get.plug_state and self.data.set.plug_state_prev is False: - try: - self.data.set.ocpp_transaction_id = data.data.optional_data.start_transaction( - self.data.config.ocpp_chargebox_id, - self.num, - self.data.set.rfid, - self.data.get.imported) - Pub().pub("openWB/set/chargepoint/"+str(self.num) + - "/set/ocpp_transaction_id", self.data.set.ocpp_transaction_id) - except Exception: - log.exception("Fehler im OCPP-Modul _start_transaction()") + if ((self.data.get.plug_state and self.data.set.plug_state_prev is False) or + (self.data.set.ocpp_transaction_id is None and self.data.get.charge_state)): + self.data.set.ocpp_transaction_id = data.data.optional_data.start_transaction( + self.data.config.ocpp_chargebox_id, + self.chargepoint_module.fault_state, + self.num, + self.data.set.rfid, + self.data.get.imported) + Pub().pub("openWB/set/chargepoint/"+str(self.num) + + "/set/ocpp_transaction_id", self.data.set.ocpp_transaction_id) # SoC nach Anstecken aktualisieren if ((self.data.get.plug_state and self.data.set.plug_state_prev is False) or (self.data.get.plug_state is False and self.data.set.plug_state_prev)): diff --git a/packages/control/ocpp.py b/packages/control/ocpp.py index 66f49e0053..64a565de98 100644 --- a/packages/control/ocpp.py +++ b/packages/control/ocpp.py @@ -8,6 +8,7 @@ from control import data from control.optional_data import OptionalProtocol +from modules.common.fault_state import FaultState log = logging.getLogger(__name__) @@ -19,58 +20,70 @@ def _get_formatted_time(self: OptionalProtocol) -> str: def _process_call(self: OptionalProtocol, chargebox_id: str, + fault_state: FaultState, func: Callable) -> Optional[websockets.WebSocketClientProtocol]: async def make_call() -> websockets.WebSocketClientProtocol: - async with websockets.connect(self.data.ocpp.url+chargebox_id, subprotocols=[self.data.ocpp.version]) as ws: + async with websockets.connect(self.data.ocpp.url+chargebox_id, + subprotocols=[self.data.ocpp.version]) as ws: try: cp = OcppChargepoint(chargebox_id, ws, 2) await cp.call(func) except asyncio.exceptions.TimeoutError: # log.exception("Erwarteter TimeOut StartTransaction") pass - except websockets.exceptions.InvalidStatusCode: - raise Exception( - f"Chargebox ID {chargebox_id} konnte nicht im OCPP-Central System gefunden werden.") return ws - if self.data.ocpp.active and chargebox_id: - return asyncio.run(make_call()) + try: + if self.data.ocpp.active and chargebox_id: + return asyncio.run(make_call()) + except websockets.exceptions.InvalidStatusCode: + fault_state.warning(f"Chargebox ID {chargebox_id} konnte nicht im OCPP-Backend gefunden werden oder " + "URL des Backends ist falsch.") return None def boot_notification(self: OptionalProtocol, chargebox_id: str, + fault_state: FaultState, model: str, serial_number: str) -> Optional[int]: try: - self._process_call(chargebox_id, call.BootNotification( + self._process_call(chargebox_id, fault_state, call.BootNotification( charge_point_model=model, charge_point_vendor="openWB", firmware_version=data.data.system_data["system"].data["version"], meter_serial_number=serial_number )) - except Exception: - log.exception("Fehler OCPP: _start_transaction") + except Exception as e: + fault_state.from_exception(e) def start_transaction(self: OptionalProtocol, chargebox_id: str, + fault_state: FaultState, connector_id: int, id_tag: str, imported: int) -> Optional[int]: try: - ws = self._process_call(chargebox_id, call.StartTransaction( + ws = self._process_call(chargebox_id, fault_state, call.StartTransaction( connector_id=connector_id, id_tag=id_tag if id_tag else "", meter_start=int(imported), timestamp=self._get_formatted_time() )) if ws: - return json.loads(ws.messages[0])[2]["transactionId"] - except Exception: - log.exception("Fehler OCPP: _start_transaction") + tansaction_id = json.loads(ws.messages[0])[2]["transactionId"] + log.debug(f"Transaction ID: {tansaction_id} für Chargebox ID: {chargebox_id} mit Tag: {id_tag} und " + f"Zählerstand: {imported} erhalten.") + return tansaction_id + except Exception as e: + fault_state.from_exception(e) return None - def transfer_values(self: OptionalProtocol, chargebox_id: str, connector_id: int, imported: int) -> None: + def transfer_values(self: OptionalProtocol, + chargebox_id: str, + fault_state: FaultState, + connector_id: int, + imported: int) -> None: try: - self._process_call(chargebox_id, call.MeterValues( + self._process_call(chargebox_id, fault_state, call.MeterValues( connector_id=connector_id, meter_value=[{"timestamp": self._get_formatted_time(), "sampledValue": [ @@ -83,26 +96,31 @@ def transfer_values(self: OptionalProtocol, chargebox_id: str, connector_id: int }, ]}], )) - except Exception: - log.exception("Fehler OCPP: _transfer_values") + log.debug(f"Zählerstand {imported} an Chargebox ID: {chargebox_id} übermittelt.") + except Exception as e: + fault_state.from_exception(e) - def send_heart_beat(self: OptionalProtocol, chargebox_id: str) -> None: + def send_heart_beat(self: OptionalProtocol, chargebox_id: str, fault_state: FaultState) -> None: try: - self._process_call(chargebox_id, call.Heartbeat()) - except Exception: - log.exception("Fehler OCPP: _send_heart_beat") + self._process_call(chargebox_id, fault_state, call.Heartbeat()) + log.debug(f"Heartbeat an Chargebox ID: {chargebox_id} gesendet.") + except Exception as e: + fault_state.from_exception(e) def stop_transaction(self: OptionalProtocol, chargebox_id: str, + fault_state: FaultState, imported: int, transaction_id: int, id_tag: str) -> None: try: - self._process_call(chargebox_id, call.StopTransaction(meter_stop=int(imported), - timestamp=self._get_formatted_time(), - transaction_id=transaction_id, - reason="EVDisconnected", - id_tag=id_tag if id_tag else "" - )) - except Exception: - log.exception("Fehler OCPP: _stop_transaction") + self._process_call(chargebox_id, fault_state, call.StopTransaction(meter_stop=int(imported), + timestamp=self._get_formatted_time(), + transaction_id=transaction_id, + reason="EVDisconnected", + id_tag=id_tag if id_tag else "" + )) + log.debug(f"Transaction mit ID: {transaction_id} für Chargebox ID: {chargebox_id} mit Tag: {id_tag} und " + f"Zählerstand: {imported} beendet.") + except Exception as e: + fault_state.from_exception(e) diff --git a/packages/control/optional.py b/packages/control/optional.py index 5dbd35837b..d001ffe818 100644 --- a/packages/control/optional.py +++ b/packages/control/optional.py @@ -103,15 +103,17 @@ def ocpp_transfer_meter_values(self): def _transfer_meter_values(self): for cp in data.data.cp_data.values(): - if self.ocpp_boot_notification_send is False: - # Boot-Notfification nicht in der init-Funktion aufrufen, da noch nicht alles initialisiert ist - self.boot_notification(cp.data.config.ocpp_chargebox_id, - cp.chargepoint_module.config.type, - cp.data.get.serial_number) - self.ocpp_boot_notification_send = True - if cp.data.set.ocpp_transaction_id is not None: - try: - self.send_heart_beat(cp.data.config.ocpp_chargebox_id) - self.transfer_values(cp.data.config.ocpp_chargebox_id, cp.num, int(cp.data.get.imported)) - except Exception: - log.exception("Fehler Trigger Meter Values") + try: + if self.ocpp_boot_notification_send is False: + # Boot-Notfification nicht in der init-Funktion aufrufen, da noch nicht alles initialisiert ist + self.boot_notification(cp.data.config.ocpp_chargebox_id, + cp.chargepoint_module.fault_state, + cp.chargepoint_module.config.type, + cp.data.get.serial_number) + self.ocpp_boot_notification_send = True + if cp.data.set.ocpp_transaction_id is not None: + self.send_heart_beat(cp.data.config.ocpp_chargebox_id, cp.chargepoint_module.fault_state) + self.transfer_values(cp.data.config.ocpp_chargebox_id, + cp.chargepoint_module.fault_state, cp.num, int(cp.data.get.imported)) + except Exception as e: + cp.set_error_and_log(str(e)) diff --git a/packages/control/process.py b/packages/control/process.py index 5831b01c08..0a742fe277 100644 --- a/packages/control/process.py +++ b/packages/control/process.py @@ -10,6 +10,7 @@ from control.chargepoint.chargepoint_state import ChargepointState from helpermodules.pub import Pub from helpermodules.utils._thread_handler import joined_thread_handler +from modules.common.fault_state_level import FaultStateLevel log = logging.getLogger(__name__) @@ -53,6 +54,8 @@ def process_algorithm_results(self) -> None: f"openWB/set/chargepoint/{cp.num}/get/state_str", "Ladevorgang wurde gestartet... (bei Problemen: Prüfe bitte zuerst in den Einstellungen" " 'Ladeeinstellungen' und 'Konfiguration'.)") + if cp.chargepoint_module.fault_state.fault_state != FaultStateLevel.NO_ERROR: + cp.chargepoint_module.fault_state.store_error() modules_threads.append(self._start_charging(cp)) except Exception: log.exception("Fehler im Process-Modul für Ladepunkt "+str(cp)) diff --git a/packages/modules/chargepoints/mqtt/chargepoint_module.py b/packages/modules/chargepoints/mqtt/chargepoint_module.py index 65c7af517b..769376a064 100644 --- a/packages/modules/chargepoints/mqtt/chargepoint_module.py +++ b/packages/modules/chargepoints/mqtt/chargepoint_module.py @@ -20,8 +20,7 @@ def __init__(self, config: Mqtt) -> None: f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) def set_current(self, current: float) -> None: - with SingleComponentUpdateContext(self.fault_state): - log.debug("MQTT-Ladepunkte abonnieren die Soll-Stromstärke direkt vom Broker.") + log.debug("MQTT-Ladepunkte abonnieren die Soll-Stromstärke direkt vom Broker.") def get_values(self) -> None: with SingleComponentUpdateContext(self.fault_state): From b00235a440afb6ddfbec717b293036e155b99a8c Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 2 Oct 2024 09:24:45 +0200 Subject: [PATCH 3/8] fix mac --- packages/control/chargepoint/chargepoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index 66d86b708c..440d782821 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -729,7 +729,7 @@ def update(self, ev_list: Dict[str, Ev]) -> None: self.data.config.ocpp_chargebox_id, self.chargepoint_module.fault_state, self.num, - self.data.set.rfid, + self.data.set.rfid or self.data.get.rfid or self.data.get.vehicle_id, self.data.get.imported) Pub().pub("openWB/set/chargepoint/"+str(self.num) + "/set/ocpp_transaction_id", self.data.set.ocpp_transaction_id) From 6d7ff7938f4d8519564cf97b66494f4bbfedadea Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 2 Oct 2024 10:31:37 +0200 Subject: [PATCH 4/8] logging --- packages/helpermodules/logger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/helpermodules/logger.py b/packages/helpermodules/logger.py index 435088fa73..06bd3c6c6e 100644 --- a/packages/helpermodules/logger.py +++ b/packages/helpermodules/logger.py @@ -81,6 +81,7 @@ def mb_to_bytes(megabytes: int) -> int: logging.getLogger("pymodbus").setLevel(logging.WARNING) logging.getLogger("uModbus").setLevel(logging.WARNING) + logging.getLogger("websockets").setLevel(logging.WARNING) def threading_excepthook(args): logging.getLogger(__name__).error("Uncaught exception in threading.excepthook:", exc_info=( From d5cb16d24e05acf998f684fe3170891a729fcf92 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Wed, 2 Oct 2024 10:38:42 +0200 Subject: [PATCH 5/8] flake8 --- packages/control/chargepoint/chargepoint.py | 1 - packages/control/ocpp_test.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index 440d782821..a657886f3e 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -32,7 +32,6 @@ from control.ev import Ev from control import phase_switch from control.chargepoint.chargepoint_state import ChargepointState -from helpermodules.constants import NO_ERROR from helpermodules.phase_mapping import convert_single_evu_phase_to_cp_phase from helpermodules.pub import Pub from helpermodules import timecheck diff --git a/packages/control/ocpp_test.py b/packages/control/ocpp_test.py index d7d1a9fda1..9ecc7b0084 100644 --- a/packages/control/ocpp_test.py +++ b/packages/control/ocpp_test.py @@ -3,8 +3,8 @@ from control import data from control.chargepoint.chargepoint import Chargepoint -from modules.devices.mqtt.config import Mqtt -from modules.devices.mqtt.device import create_device +from modules.devices.generic.mqtt.config import Mqtt +from modules.devices.generic.mqtt.device import create_device @pytest.fixture() From 84867395f23395629b6e3d132ba4f687c1259a1c Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Mon, 7 Oct 2024 11:57:25 +0200 Subject: [PATCH 6/8] fix test --- packages/control/ocpp_test.py | 24 ++++++++++++++++-------- packages/control/optional.py | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/control/ocpp_test.py b/packages/control/ocpp_test.py index 9ecc7b0084..e54941c028 100644 --- a/packages/control/ocpp_test.py +++ b/packages/control/ocpp_test.py @@ -3,8 +3,11 @@ from control import data from control.chargepoint.chargepoint import Chargepoint -from modules.devices.generic.mqtt.config import Mqtt -from modules.devices.generic.mqtt.device import create_device +from control.chargepoint.chargepoint_template import CpTemplate +from control.counter import Counter +from control.ev import Ev +from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule +from modules.chargepoints.mqtt.config import Mqtt @pytest.fixture() @@ -18,17 +21,17 @@ def test_start_transaction(mock_data, monkeypatch): cp = Chargepoint(1, None) cp.data.config.ocpp_chargebox_id = "cp1" cp.data.get.plug_state = True + cp.template = CpTemplate() + cp.chargepoint_module = ChargepointModule(Mqtt()) start_transaction_mock = Mock() monkeypatch.setattr(data.data.optional_data, "start_transaction", start_transaction_mock) _pub_configured_ev_mock = Mock() monkeypatch.setattr(cp, "_pub_configured_ev", _pub_configured_ev_mock) - prepare_cp_mock = Mock(return_value=(-1, None)) - monkeypatch.setattr(cp, "prepare_cp", prepare_cp_mock) cp.update([]) - assert start_transaction_mock.call_args == (("cp1", 1, None, 0),) + assert start_transaction_mock.call_args == (("cp1", cp.chargepoint_module.fault_state, 1, None, 0),) def test_stop_transaction(mock_data, monkeypatch): @@ -36,21 +39,26 @@ def test_stop_transaction(mock_data, monkeypatch): cp.data.config.ocpp_chargebox_id = "cp1" cp.data.get.plug_state = False cp.data.set.ocpp_transaction_id = 124 - cp.data.set.charging_ev_prev = -1 + cp.data.set.charging_ev_prev = 1 + cp.chargepoint_module = ChargepointModule(Mqtt()) + cp.template = CpTemplate() stop_transaction_mock = Mock() monkeypatch.setattr(data.data.optional_data, "stop_transaction", stop_transaction_mock) + get_evu_counter_mock = Mock(return_value=Mock(spec=Counter)) + monkeypatch.setattr(data.data.counter_all_data, "get_evu_counter", get_evu_counter_mock) + data.data.ev_data["ev1"] = Ev(1) cp._process_charge_stop() - assert stop_transaction_mock.call_args == (("cp1", 0, 124, None),) + assert stop_transaction_mock.call_args == (("cp1", cp.chargepoint_module.fault_state, 0, 124, None),) def test_send_ocpp_data(mock_data, monkeypatch): data.data.cp_data["cp1"] = Chargepoint(1, None) data.data.cp_data["cp1"].data.config.ocpp_chargebox_id = "cp1" data.data.cp_data["cp1"].data.get.plug_state = True - data.data.cp_data["cp1"].chargepoint_module = create_device(Mqtt()) + data.data.cp_data["cp1"].chargepoint_module = ChargepointModule(Mqtt()) data.data.cp_data["cp1"].data.get.serial_number = "123456" transfer_values_mock = Mock() monkeypatch.setattr(data.data.optional_data, "transfer_values", transfer_values_mock) diff --git a/packages/control/optional.py b/packages/control/optional.py index d001ffe818..0f252a55cf 100644 --- a/packages/control/optional.py +++ b/packages/control/optional.py @@ -115,5 +115,5 @@ def _transfer_meter_values(self): self.send_heart_beat(cp.data.config.ocpp_chargebox_id, cp.chargepoint_module.fault_state) self.transfer_values(cp.data.config.ocpp_chargebox_id, cp.chargepoint_module.fault_state, cp.num, int(cp.data.get.imported)) - except Exception as e: - cp.set_error_and_log(str(e)) + except Exception: + log.exception("Fehler im OCPP-Optional-Modul") From ddcdbe02921a8608e280f9d6ebad06ea702683ec Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Tue, 8 Oct 2024 17:17:59 +0200 Subject: [PATCH 7/8] clean up imports --- packages/helpermodules/subdata.py | 11 ++--------- packages/main.py | 16 ++++------------ 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index 3381780a71..47965f0be4 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -9,26 +9,19 @@ import subprocess import paho.mqtt.client as mqtt -from control import bat_all, bat, pv_all +from control import bat_all, bat, counter, counter_all, ev, general, optional, pv, pv_all from control.chargepoint import chargepoint -from control import counter -from control import counter_all -from control import ev -from control import general from control.chargepoint.chargepoint_all import AllChargepoints from control.chargepoint.chargepoint_data import Log from control.chargepoint.chargepoint_state_update import ChargepointStateUpdate from control.chargepoint.chargepoint_template import CpTemplate, CpTemplateData -from helpermodules import graph +from helpermodules import graph, system from helpermodules.abstract_plans import AutolockPlan from helpermodules.broker import InternalBrokerClient from helpermodules.messaging import MessageType, pub_system_message from helpermodules.utils.run_command import run_command from helpermodules.utils.topic_parser import decode_payload, get_index, get_second_index -from control import optional from helpermodules.pub import Pub -from helpermodules import system -from control import pv from dataclass_utils import dataclass_from_dict from modules.common.abstract_vehicle import CalculatedSocState, GeneralVehicleConfig from modules.common.configurable_backup_cloud import ConfigurableBackupCloud diff --git a/packages/main.py b/packages/main.py index 659d5aafd4..e191d5674f 100755 --- a/packages/main.py +++ b/packages/main.py @@ -18,24 +18,16 @@ from threading import Thread from control.chargelog.chargelog import calculate_charge_cost +from control import data, prepare, process +from control.algorithm import algorithm +from helpermodules import command, setdata, subdata, timecheck, update_config from helpermodules.changed_values_handler import ChangedValuesContext from helpermodules.measurement_logging.update_yields import update_daily_yields, update_pv_monthly_yearly_yields from helpermodules.measurement_logging.write_log import LogType, save_log -from modules import loadvars -from modules import configuration -from helpermodules import timecheck, update_config -from helpermodules import subdata -from helpermodules import setdata -from helpermodules import command from helpermodules.modbusserver import start_modbus_server from helpermodules.pub import Pub -from control import prepare -from control import data -from control import process -from control.algorithm import algorithm -from control import optional from helpermodules.utils import exit_after -from modules import update_soc +from modules import configuration, loadvars, update_soc from modules.internal_chargepoint_handler.internal_chargepoint_handler import GeneralInternalChargepointHandler from modules.internal_chargepoint_handler.rfid import RfidReader from modules.utils import wait_for_module_update_completed From 5a7e63f91559bfd7e48772f38cd0954ed94053ce Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Tue, 8 Oct 2024 18:10:08 +0200 Subject: [PATCH 8/8] typo, json config --- packages/control/ocpp_test.py | 4 ++-- packages/control/optional.py | 6 +++--- packages/control/optional_data.py | 4 ++-- packages/helpermodules/setdata.py | 9 +++------ packages/helpermodules/subdata.py | 4 ++++ packages/helpermodules/update_config.py | 7 +++---- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/control/ocpp_test.py b/packages/control/ocpp_test.py index e54941c028..d9e3efa51f 100644 --- a/packages/control/ocpp_test.py +++ b/packages/control/ocpp_test.py @@ -67,11 +67,11 @@ def test_send_ocpp_data(mock_data, monkeypatch): send_heart_beat_mock = Mock() monkeypatch.setattr(data.data.optional_data, "send_heart_beat", send_heart_beat_mock) - data.data.optional_data.ocpp_boot_notification_send = False + data.data.optional_data.ocpp_boot_notification_sent = False data.data.optional_data._transfer_meter_values() boot_notification_mock.call_args == (("cp1", "mqtt", "123456"),) send_heart_beat_mock.call_args == (("cp1",),) transfer_values_mock.call_args == (("cp1", 1, 0),) - assert data.data.optional_data.ocpp_boot_notification_send is True + assert data.data.optional_data.ocpp_boot_notification_sent is True diff --git a/packages/control/optional.py b/packages/control/optional.py index 0f252a55cf..8a33678668 100644 --- a/packages/control/optional.py +++ b/packages/control/optional.py @@ -25,7 +25,7 @@ def __init__(self): self.et_module: ConfigurableElectricityTariff = None self.data.dc_charging = hardware_configuration.get_hardware_configuration_setting("dc_charging") Pub().pub("openWB/optional/dc_charging", self.data.dc_charging) - self.ocpp_boot_notification_send = False + self.ocpp_boot_notification_sent = False except Exception: log.exception("Fehler im Optional-Modul") @@ -104,13 +104,13 @@ def ocpp_transfer_meter_values(self): def _transfer_meter_values(self): for cp in data.data.cp_data.values(): try: - if self.ocpp_boot_notification_send is False: + if self.ocpp_boot_notification_sent is False: # Boot-Notfification nicht in der init-Funktion aufrufen, da noch nicht alles initialisiert ist self.boot_notification(cp.data.config.ocpp_chargebox_id, cp.chargepoint_module.fault_state, cp.chargepoint_module.config.type, cp.data.get.serial_number) - self.ocpp_boot_notification_send = True + self.ocpp_boot_notification_sent = True if cp.data.set.ocpp_transaction_id is not None: self.send_heart_beat(cp.data.config.ocpp_chargebox_id, cp.chargepoint_module.fault_state) self.transfer_values(cp.data.config.ocpp_chargebox_id, diff --git a/packages/control/optional_data.py b/packages/control/optional_data.py index 39e0a473e2..80f7ac4dd3 100644 --- a/packages/control/optional_data.py +++ b/packages/control/optional_data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Dict, Protocol +from typing import Dict, Optional, Protocol from dataclass_utils.factories import empty_dict_factory from helpermodules.constants import NO_ERROR @@ -61,7 +61,7 @@ def rfid_factory() -> Rfid: @dataclass class Ocpp: active: bool = False - url: str = "" + url: Optional[str] = None version: str = "ocpp1.6" diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index 03c4429728..3a09686ea1 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -854,14 +854,11 @@ def process_optional_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, int, [(0, 2)]) elif "openWB/set/optional/et/get/fault_str" in msg.topic: self._validate_value(msg, str) - elif "openWB/set/optional/et/provider" in msg.topic: + elif ("openWB/set/optional/et/provider" in msg.topic or + "openWB/set/optional/ocpp/config" in msg.topic): self._validate_value(msg, "json") - elif ("openWB/set/optional/rfid/active" in msg.topic or - "openWB/set/optional/ocpp/active" in msg.topic): + elif "openWB/set/optional/rfid/active" in msg.topic: self._validate_value(msg, bool) - elif ("openWB/set/optional/ocpp/url" in msg.topic or - "openWB/set/optional/ocpp/version" in msg.topic): - self._validate_value(msg, str) elif "openWB/set/optional/int_display/rotation" in msg.topic: self._validate_value(msg, int, [(0, 0), (90, 90), (180, 180), (270, 270)]) elif "openWB/set/optional/int_display/active" in msg.topic: diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index 47965f0be4..82a99dfb7b 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -15,6 +15,7 @@ from control.chargepoint.chargepoint_data import Log from control.chargepoint.chargepoint_state_update import ChargepointStateUpdate from control.chargepoint.chargepoint_template import CpTemplate, CpTemplateData +from control.optional_data import Ocpp from helpermodules import graph, system from helpermodules.abstract_plans import AutolockPlan from helpermodules.broker import InternalBrokerClient @@ -678,6 +679,9 @@ def process_optional_topic(self, var: optional.Optional, msg: mqtt.MQTTMessage): var.et_get_prices() else: self.set_json_payload_class(var.data.et, msg) + elif re.search("/optional/ocpp/", msg.topic) is not None: + config_dict = decode_payload(msg.payload) + var.data.ocpp = dataclass_from_dict(Ocpp, config_dict) else: self.set_json_payload_class(var.data, msg) except Exception: diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index aaf6baadd5..2295fd0132 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -11,6 +11,7 @@ from control.bat_all import BatConsiderationMode from control.chargepoint.charging_type import ChargingType from control.general import ChargemodeConfig +from control.optional_data import Ocpp import dataclass_utils from control.chargepoint.chargepoint_template import get_chargepoint_template_default @@ -254,9 +255,7 @@ class UpdateConfig: "^openWB/optional/int_display/only_local_charge_points", "^openWB/optional/led/active$", "^openWB/optional/rfid/active$", - "^openWB/optional/ocpp/active$", - "^openWB/optional/ocpp/url$", - "^openWB/optional/ocpp/version$", + "^openWB/optional/ocpp/config$", "^openWB/pv/config/configured$", "^openWB/pv/get/exported$", @@ -504,7 +503,7 @@ class UpdateConfig: ("openWB/optional/int_display/theme", dataclass_utils.asdict(CardsDisplayTheme())), ("openWB/optional/int_display/only_local_charge_points", False), ("openWB/optional/led/active", False), - ("openWB/optional/ocpp/active", False), + ("openWB/optional/ocpp/config", dataclass_utils.asdict(Ocpp())), ("openWB/optional/rfid/active", False), ("openWB/system/backup_cloud/config", NO_MODULE), ("openWB/system/backup_cloud/backup_before_update", True),