diff --git a/packages/control/bat_all.py b/packages/control/bat_all.py
index ea4020dddd..d1085b0d2b 100644
--- a/packages/control/bat_all.py
+++ b/packages/control/bat_all.py
@@ -291,40 +291,42 @@ def set_power_limit_controllable(self):
def get_power_limit(self):
if self.data.config.bat_control_permitted is False:
- return
- chargepoint_by_chargemodes = get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_CHARGING)
- # Falls aktive Steuerung an und Fahrzeuge laden und kein Überschuss im System ist,
- # dann Speicherleistung begrenzen.
- if (self.data.config.power_limit_mode != BatPowerLimitMode.NO_LIMIT.value and
- len(chargepoint_by_chargemodes) > 0 and
- data.data.cp_all_data.data.get.power > 100 and
- self.data.get.power_limit_controllable and
- self.data.get.power <= 0 and
- data.data.counter_all_data.get_evu_counter().data.get.power >= -100):
- if self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_STOP.value:
- self.data.set.power_limit = 0
- elif self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_TO_HOME_CONSUMPTION.value:
- self.data.set.power_limit = data.data.counter_all_data.data.set.home_consumption * -1
- log.debug(f"Speicher-Leistung begrenzen auf {self.data.set.power_limit/1000}kW")
- else:
self.data.set.power_limit = None
- control_range_low = data.data.general_data.data.chargemode_config.pv_charging.control_range[0]
- control_range_high = data.data.general_data.data.chargemode_config.pv_charging.control_range[1]
- control_range_center = control_range_high - (control_range_high - control_range_low) / 2
- if len(chargepoint_by_chargemodes) == 0:
- log.debug("Speicher-Leistung nicht begrenzen, "
- "da keine Ladepunkte in einem Lademodus mit Netzbezug sind.")
- elif data.data.cp_all_data.data.get.power <= 100:
- log.debug("Speicher-Leistung nicht begrenzen, da kein Ladepunkt mit Netzbezug lädt.")
- elif self.data.get.power_limit_controllable is False:
- log.debug("Speicher-Leistung nicht begrenzen, da keine regelbaren Speicher vorhanden sind.")
- elif self.data.get.power > 0:
- log.debug("Speicher-Leistung nicht begrenzen, da kein Speicher entladen wird.")
- elif data.data.counter_all_data.get_evu_counter().data.get.power < control_range_center + 80:
- # Wenn der Regelbereich zB auf Bezug steht, darf auch die Leistung des Regelbereichs entladen werden.
- log.debug("Speicher-Leistung nicht begrenzen, da EVU-Überschuss vorhanden ist.")
+ else:
+ chargepoint_by_chargemodes = get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_CHARGING)
+ # Falls aktive Steuerung an und Fahrzeuge laden und kein Überschuss im System ist,
+ # dann Speicherleistung begrenzen.
+ if (self.data.config.power_limit_mode != BatPowerLimitMode.NO_LIMIT.value and
+ len(chargepoint_by_chargemodes) > 0 and
+ data.data.cp_all_data.data.get.power > 100 and
+ self.data.get.power_limit_controllable and
+ self.data.get.power <= 0 and
+ data.data.counter_all_data.get_evu_counter().data.get.power >= -100):
+ if self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_STOP.value:
+ self.data.set.power_limit = 0
+ elif self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_TO_HOME_CONSUMPTION.value:
+ self.data.set.power_limit = data.data.counter_all_data.data.set.home_consumption * -1
+ log.debug(f"Speicher-Leistung begrenzen auf {self.data.set.power_limit/1000}kW")
else:
- log.debug("Speicher-Leistung nicht begrenzen.")
+ self.data.set.power_limit = None
+ control_range_low = data.data.general_data.data.chargemode_config.pv_charging.control_range[0]
+ control_range_high = data.data.general_data.data.chargemode_config.pv_charging.control_range[1]
+ control_range_center = control_range_high - (control_range_high - control_range_low) / 2
+ if len(chargepoint_by_chargemodes) == 0:
+ log.debug("Speicher-Leistung nicht begrenzen, "
+ "da keine Ladepunkte in einem Lademodus mit Netzbezug sind.")
+ elif data.data.cp_all_data.data.get.power <= 100:
+ log.debug("Speicher-Leistung nicht begrenzen, da kein Ladepunkt mit Netzbezug lädt.")
+ elif self.data.get.power_limit_controllable is False:
+ log.debug("Speicher-Leistung nicht begrenzen, da keine regelbaren Speicher vorhanden sind.")
+ elif self.data.get.power > 0:
+ log.debug("Speicher-Leistung nicht begrenzen, da kein Speicher entladen wird.")
+ elif data.data.counter_all_data.get_evu_counter().data.get.power < control_range_center + 80:
+ # Wenn der Regelbereich zB auf Bezug steht, darf auch die Leistung des Regelbereichs entladen
+ # werden.
+ log.debug("Speicher-Leistung nicht begrenzen, da EVU-Überschuss vorhanden ist.")
+ else:
+ log.debug("Speicher-Leistung nicht begrenzen.")
remaining_power_limit = self.data.set.power_limit
for bat_component in get_controllable_bat_components():
if self.data.set.power_limit is None:
diff --git a/packages/control/chargelog/chargelog.py b/packages/control/chargelog/chargelog.py
index 4dcad2e8b2..b8605e058d 100644
--- a/packages/control/chargelog/chargelog.py
+++ b/packages/control/chargelog/chargelog.py
@@ -2,6 +2,7 @@
from enum import Enum
import json
import logging
+import os
import pathlib
from typing import Any, Dict, List, Optional
@@ -250,13 +251,18 @@ def write_new_entry(new_entry):
filepath = str(_get_parent_file() / "data" / "charge_log" /
(timecheck.create_timestamp_YYYYMM() + ".json"))
try:
- with open(filepath, "r", encoding="utf-8") as json_file:
- content = json.load(json_file)
+ if os.path.exists(filepath) and os.path.getsize(filepath) == 0:
+ content = []
+ else:
+ with open(filepath, "r", encoding="utf-8") as json_file:
+ try:
+ content = json.load(json_file)
+ except json.decoder.JSONDecodeError:
+ corrupt_path = f"{filepath}.unparsable_{timecheck.create_timestamp()}"
+ os.rename(filepath, corrupt_path)
+ log.error(f"ChargeLog: Korrupte Datei umbenannt nach {corrupt_path}")
+ content = []
except FileNotFoundError:
- # with open(filepath, "w", encoding="utf-8") as jsonFile:
- # json.dump([], jsonFile)
- # with open(filepath, "r", encoding="utf-8") as jsonFile:
- # content = json.load(jsonFile)
content = []
content.append(new_entry)
write_and_check(filepath, content)
diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py
index 8dedab09c4..0d403dea61 100644
--- a/packages/control/chargepoint/chargepoint.py
+++ b/packages/control/chargepoint/chargepoint.py
@@ -160,13 +160,15 @@ def _is_manual_lock_inactive(self) -> Tuple[bool, Optional[str]]:
# Vergleiche werden case-insensitive durchgeführt
# das vereinfacht die Eingabe, kann aber auch für falsche Treffer sorgen.
# 'fnmatch()' ist case-insensitive
+ match = False
for tag_id in self.template.data.valid_tags:
if ((self.data.get.rfid is not None and fnmatch(self.data.get.rfid, tag_id)) or
(self.data.get.vehicle_id is not None and fnmatch(self.data.get.vehicle_id, tag_id)) or
(self.data.set.rfid is not None and fnmatch(self.data.set.rfid, tag_id))):
- Pub().pub(f"openWB/set/chargepoint/{self.num}/set/manual_lock", False)
- # Wenn der Ladepunkt nach dem Abstecken gesperrt werden soll, und kein Fahrzeug angeschlossen ist wird gesperrt
- if self.template.data.disable_after_unplug and self.data.get.plug_state is False:
+ match = True
+ if match:
+ Pub().pub(f"openWB/set/chargepoint/{self.num}/set/manual_lock", False)
+ elif self.template.data.disable_after_unplug and self.data.get.plug_state is False:
Pub().pub(f"openWB/set/chargepoint/{self.num}/set/manual_lock", True)
if self.data.set.manual_lock:
@@ -374,7 +376,7 @@ def _is_phase_switch_required(self) -> bool:
self.check_deviating_contactor_states(self.data.set.phases_to_use,
self.data.control_parameter.phases)) and
# Wenn der Ladevorgang gestartet wird, muss vor dem ersten Laden umgeschaltet werden.
- self.data.set.current != 0):
+ self.data.set.current != 0 and self.data.get.charge_state is False):
phase_switch_required = True
if phase_switch_required:
# Umschaltung fehlgeschlagen
@@ -383,18 +385,18 @@ def _is_phase_switch_required(self) -> bool:
if self.data.control_parameter.failed_phase_switches > self.MAX_FAILED_PHASE_SWITCHES:
phase_switch_required = False
self.set_state_and_log(
- "Keine Phasenumschaltung, da die maximale Anzahl an Fehlversuchen erreicht wurde. Die "
- "aktuelle Phasenzahl wird bis zum Abstecken beibehalten.")
+ "Keine Phasenumschaltung, da die maximale Anzahl an Fehlversuchen erreicht wurde.")
self.data.control_parameter.failed_phase_switches += 1
else:
# Umschaltung vor Ladestart zulassen
- if self.data.set.log.imported_since_plugged != 0:
+ if (self.data.set.log.imported_since_plugged != 0 and
+ self.data.control_parameter.failed_phase_switches > 0):
phase_switch_required = False
self.set_state_and_log(
"Keine Phasenumschaltung, da wiederholtes Anstoßen der Umschaltung in den übergreifenden "
"Ladeeinstellungen deaktiviert wurde. Die aktuelle "
"Phasenzahl wird bis zum Abstecken beibehalten.")
- self.data.control_parameter.failed_phase_switches += 1
+ self.data.control_parameter.failed_phase_switches += 1
return phase_switch_required
STOP_CHARGING = ", dafür wird die Ladung unterbrochen."
@@ -723,6 +725,8 @@ def update(self, ev_list: Dict[str, Ev]) -> None:
data.data.counter_all_data.get_evu_counter().reset_switch_on_off(
self, charging_ev)
charging_ev.reset_phase_switch(self.data.control_parameter)
+ if self.chargemode_changed:
+ self.data.control_parameter.failed_phase_switches = 0
message = message_ev if message_ev else message
# Ein Eintrag muss nur erstellt werden, wenn vorher schon geladen wurde und auch danach noch
# geladen werden soll.
@@ -823,8 +827,9 @@ def _get_charging_ev(self, vehicle: int, ev_list: Dict[str, Ev]) -> Ev:
if self.data.set.charging_ev_prev != vehicle:
Pub().pub(f"openWB/set/vehicle/{charging_ev.num}/get/force_soc_update", True)
log.debug("SoC nach EV-Wechsel")
- self.update_charge_template(charging_ev.charge_template)
- if self.data.set.charge_template.data.id != charging_ev.charge_template.data.id:
+ # wenn vorher kein anderes Fahrzeug zugeordnet war, Ladeprofil nicht zurücksetzen
+ if ((self.data.set.charging_ev_prev != vehicle and self.data.set.charging_ev_prev != -1) or
+ (self.data.set.charge_template.data.id != charging_ev.charge_template.data.id)):
self.update_charge_template(charging_ev.charge_template)
self.data.set.charging_ev_data = charging_ev
self.data.set.charging_ev = vehicle
@@ -908,7 +913,8 @@ def cp_ev_chargemode_support_phase_switch(self) -> bool:
def cp_ev_support_phase_switch(self) -> bool:
return (self.data.config.auto_phase_switch_hw and
self.data.get.evse_signaling != EvseSignaling.HLC and
- self.data.set.charging_ev_data.ev_template.data.prevent_phase_switch is False)
+ (self.data.set.charging_ev_data.ev_template.data.prevent_phase_switch is False or
+ self.data.set.log.imported_since_plugged == 0))
def chargemode_support_phase_switch(self) -> bool:
control_parameter = self.data.control_parameter
@@ -934,7 +940,7 @@ def failed_phase_switches_reached(self) -> bool:
(data.data.general_data.data.chargemode_config.retry_failed_phase_switches is False and
self.data.control_parameter.failed_phase_switches == 1)):
self.set_state_and_log(
- "Keine automatische Umschaltung, da die maximale Anzahl an Fehlversuchen erreicht wurde. ")
+ "Keine Phasenumschaltung, da die maximale Anzahl an Fehlversuchen erreicht wurde. ")
return False
else:
return True
diff --git a/packages/control/ev/ev.py b/packages/control/ev/ev.py
index ab74e2d326..6736002064 100644
--- a/packages/control/ev/ev.py
+++ b/packages/control/ev/ev.py
@@ -353,11 +353,13 @@ def auto_phase_switch(self,
waiting_time,
delay)[1])
control_parameter.state = ChargepointState.PHASE_SWITCH_DELAY
- elif condition_msg:
- if condition_msg == self.CURRENT_OUT_OF_NOMINAL_DIFFERENCE:
- message = f"Keine Phasenumschaltung{condition_msg}"
- else:
- log.debug(f"Keine Phasenumschaltung{condition_msg}")
+ else:
+ if condition_msg:
+ if condition_msg == self.CURRENT_OUT_OF_NOMINAL_DIFFERENCE:
+ message = f"Keine Phasenumschaltung{condition_msg}"
+ else:
+ log.debug(f"Keine Phasenumschaltung{condition_msg}")
+ control_parameter.timestamp_phase_switch_buffer_start = None
else:
if condition:
# Timer laufen lassen
@@ -379,6 +381,7 @@ def auto_phase_switch(self,
).data.set.reserved_surplus -= max(0, required_reserved_power)
message = f"Verzögerung für die {direction_str} Phasen abgebrochen{condition_msg}"
control_parameter.state = ChargepointState.CHARGING_ALLOWED
+ control_parameter.timestamp_phase_switch_buffer_start = None
if message:
log.info(f"LP {cp_num}: {message}")
diff --git a/packages/control/phase_switch.py b/packages/control/phase_switch.py
index 89d0edd407..ca4b3bc84b 100644
--- a/packages/control/phase_switch.py
+++ b/packages/control/phase_switch.py
@@ -38,8 +38,6 @@ def _perform_phase_switch(chargepoint_module: AbstractChargepoint, phases: int,
time.sleep(5)
# Phasenumschaltung entsprechend Modul
chargepoint_module.switch_phases(phases, ev.ev_template.data.phase_switch_pause)
- # Die Ladung wird in start_charging wieder gestartet, wenn phase_switch_timestamp wieder auf None gesetzt wird.
- time.sleep(ev.ev_template.data.keep_charge_active_duration)
except Exception:
log.exception("Fehler im Phasenumschaltungs-Modul")
diff --git a/packages/helpermodules/command.py b/packages/helpermodules/command.py
index 2c19972c8d..41067b8c1f 100644
--- a/packages/helpermodules/command.py
+++ b/packages/helpermodules/command.py
@@ -461,7 +461,7 @@ def addChargeTemplate(self, connection_id: str, payload: dict) -> None:
new_charge_template = asdict(new_charge_template)
else:
new_charge_template = get_new_charge_template()
- new_charge_template["id"] = new_id
+ new_charge_template["id"] = new_id
Pub().pub("openWB/set/command/max_id/charge_template", new_id)
Pub().pub(f"openWB/set/vehicle/template/charge_template/{new_id}", new_charge_template)
@@ -648,8 +648,9 @@ def addEvTemplate(self, connection_id: str, payload: dict) -> None:
else:
new_ev_template = dataclass_utils.asdict(EvTemplateData())
new_id = self.max_id_ev_template + 1
- Pub().pub(f'openWB/set/vehicle/template/ev_template/{new_id}', new_ev_template)
+ new_ev_template["id"] = new_id
self.max_id_ev_template = new_id
+ Pub().pub(f'openWB/set/vehicle/template/ev_template/{new_id}', new_ev_template)
Pub().pub("openWB/set/command/max_id/ev_template", new_id)
pub_user_message(
payload, connection_id,
diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py
index f5ce2c377b..094067adcd 100644
--- a/packages/helpermodules/update_config.py
+++ b/packages/helpermodules/update_config.py
@@ -57,7 +57,7 @@
class UpdateConfig:
- DATASTORE_VERSION = 94
+ DATASTORE_VERSION = 96
valid_topic = [
"^openWB/bat/config/bat_control_permitted$",
@@ -647,7 +647,7 @@ def __update_topic(self, topic: str, payload):
if payload == "":
del self.all_received_topics[topic]
else:
- self.all_received_topics[topic] = payload
+ self.all_received_topics[topic] = copy.deepcopy(payload)
def __remove_outdated_topics(self):
""" remove outdated topics from all_received_topics and broker
@@ -1967,7 +1967,7 @@ def upgrade(topic: str, payload) -> None:
# replace smarteq soc module by no_module
if payload.get("type") == "smarteq":
payload = NO_MODULE
- Pub().pub(topic, payload)
+ return {topic: payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 71)
@@ -1983,7 +1983,7 @@ def upgrade(topic: str, payload) -> None:
payload["configuration"]["firmware"] = "v1"
elif payload["configuration"].get("firmware") == "v112":
payload["configuration"]["firmware"] = "v2"
- Pub().pub(topic, payload)
+ return {topic: payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 72)
@@ -1994,7 +1994,7 @@ def upgrade(topic: str, payload) -> None:
# replace bmw soc module by no_module
if payload.get("type") == "bmw":
payload = NO_MODULE
- Pub().pub(topic, payload)
+ return {topic: payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 73)
@@ -2017,7 +2017,7 @@ def upgrade(topic: str, payload) -> None:
if payload.get("type") == "solax":
if "version" not in payload["configuration"]:
payload["configuration"].update({"version": "g3"})
- Pub().pub(topic, payload)
+ return {topic: payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 75)
@@ -2129,8 +2129,8 @@ def upgrade(topic: str, payload) -> Optional[dict]:
component_topic):
component_config = decode_payload(component_payload)
if "counter" == component_config["type"]:
- Pub().pub((f"openWB/system/device/{device_config['id']}/component/"
- f"{component_config['id']}/simulation"), "")
+ return {(f"openWB/system/device/{device_config['id']}/component/"
+ f"{component_config['id']}/simulation"): ""}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 80)
@@ -2333,12 +2333,12 @@ def upgrade_datastore_86(self) -> None:
def upgrade_datastore_87(self) -> None:
def upgrade(topic: str, payload) -> None:
- if (re.search("openWB/vehicle/template/charge_template/[0-9]+", topic) is not None or
- re.search("openWB/vehicle/template/ev_template/[0-9]+", topic) is not None):
+ if (re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None or
+ re.search("openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None):
payload = decode_payload(payload)
index = int(get_index(topic))
payload.update({"id": index})
- Pub().pub(topic, payload)
+ return {topic: payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 88)
@@ -2430,6 +2430,7 @@ def upgrade_datastore_93(self) -> None:
# Pläne die keinen plans Key haben, id=None
max_id = -1
none_id = False
+ modified_topics = {}
for topic, payload in self.all_received_topics.items():
if re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None:
payload = decode_payload(payload)
@@ -2444,8 +2445,7 @@ def upgrade_datastore_93(self) -> None:
raise TypeError(f"Plan {plan} hat keinen Key 'id' und ist kein NoneType.")
except KeyError:
payload["chargemode"]["scheduled_charging"].update({"plans": []})
- self.all_received_topics[topic] = json.dumps(payload, ensure_ascii=False).encode("utf-8")
- Pub().pub(f"openWB/set/vehicle/template/charge_template/{get_index(topic)}", payload)
+ modified_topics[f"openWB/set/vehicle/template/charge_template/{get_index(topic)}"] = payload
if none_id:
for topic, payload in self.all_received_topics.items():
if re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None:
@@ -2454,8 +2454,7 @@ def upgrade_datastore_93(self) -> None:
if plan["id"] is None:
plan["id"] = max_id + 1
max_id += 1
- self.all_received_topics[topic] = json.dumps(payload, ensure_ascii=False).encode("utf-8")
- Pub().pub(f"openWB/set/vehicle/template/charge_template/{get_index(topic)}", payload)
+ modified_topics[f"openWB/vehicle/template/charge_template/{get_index(topic)}"] = payload
max_id = -1
none_id = False
@@ -2473,8 +2472,7 @@ def upgrade_datastore_93(self) -> None:
raise TypeError(f"Plan {plan} hat keinen Key 'id' und ist kein NoneType.")
except KeyError:
payload["time_charging"].update({"plans": []})
- self.all_received_topics[topic] = json.dumps(payload, ensure_ascii=False).encode("utf-8")
- Pub().pub(f"openWB/set/vehicle/template/charge_template/{get_index(topic)}", payload)
+ modified_topics[f"openWB/vehicle/template/charge_template/{get_index(topic)}"] = payload
if none_id:
for topic, payload in self.all_received_topics.items():
if re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None:
@@ -2483,8 +2481,7 @@ def upgrade_datastore_93(self) -> None:
if plan["id"] is None:
plan["id"] = max_id + 1
max_id += 1
- self.all_received_topics[topic] = json.dumps(payload, ensure_ascii=False).encode("utf-8")
- Pub().pub(f"openWB/set/vehicle/template/charge_template/{get_index(topic)}", payload)
+ modified_topics[f"openWB/vehicle/template/charge_template/{get_index(topic)}"] = payload
max_id = -1
none_id = False
@@ -2502,8 +2499,7 @@ def upgrade_datastore_93(self) -> None:
raise TypeError(f"Plan {plan} hat keinen Key 'id' und ist kein NoneType.")
except KeyError:
payload["autolock"].update({"plans": []})
- self.all_received_topics[topic] = json.dumps(payload, ensure_ascii=False).encode("utf-8")
- Pub().pub(f"openWB/set/chargepoint/template/{get_index(topic)}", payload)
+ modified_topics[f"openWB/chargepoint/template/{get_index(topic)}"] = payload
if none_id:
for topic, payload in self.all_received_topics.items():
if re.search("openWB/chargepoint/template/[0-9]+$", topic) is not None:
@@ -2512,6 +2508,48 @@ def upgrade_datastore_93(self) -> None:
if plan["id"] is None:
plan["id"] = max_id + 1
max_id += 1
- self.all_received_topics[topic] = json.dumps(payload, ensure_ascii=False).encode("utf-8")
- Pub().pub(f"openWB/set/chargepoint/template/{get_index(topic)}", payload)
+ modified_topics[f"openWB/chargepoint/template/{get_index(topic)}"] = payload
+ for topic, payload in modified_topics.items():
+ self.__update_topic(topic, payload)
self.__update_topic("openWB/system/datastore_version", 94)
+
+ def upgrade_datastore_94(self):
+ def upgrade(topic, payload):
+ ids = []
+ if re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None:
+ payload = decode_payload(payload)
+ for plan in payload["chargemode"]["scheduled_charging"]["plans"]:
+ if plan["id"] is not None:
+ ids.append(plan["id"])
+ ids.sort()
+ unique_ids = set(ids)
+ if len(ids) != len(unique_ids):
+ max_id = decode_payload(
+ self.all_received_topics["openWB/command/max_id/charge_template_scheduled_plan"])
+ for plan in payload["chargemode"]["scheduled_charging"]["plans"]:
+ try:
+ unique_ids.remove(plan["id"])
+ except KeyError:
+ max_id += 1
+ plan["id"] = max_id
+ return {topic: payload, "openWB/command/max_id/charge_template_scheduled_plan": max_id}
+ self._loop_all_received_topics(upgrade)
+ self.__update_topic("openWB/system/datastore_version", 95)
+
+ def upgrade_datastore_95(self) -> None:
+ def upgrade(topic: str, payload) -> Optional[dict]:
+ # Fix id in charge and ev templates
+ if (
+ re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None
+ or re.search("openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None
+ ):
+ payload = decode_payload(payload)
+ topic_index = int(get_index(topic))
+ if "id" not in payload or payload["id"] != topic_index:
+ log.error(
+ f"Fixing id in template {topic} from {payload.get('id')} to {topic_index}"
+ )
+ payload["id"] = topic_index
+ return {topic: payload}
+ self._loop_all_received_topics(upgrade)
+ self.__update_topic("openWB/system/datastore_version", 96)
diff --git a/packages/helpermodules/update_config_test.py b/packages/helpermodules/update_config_test.py
index 79405ebd4f..3b2addd873 100644
--- a/packages/helpermodules/update_config_test.py
+++ b/packages/helpermodules/update_config_test.py
@@ -1,3 +1,7 @@
+import json
+from pathlib import Path
+
+import pytest
from helpermodules.update_config import UpdateConfig
@@ -28,3 +32,24 @@ def test_remove_invalid_topics(mock_pub):
assert len(mock_pub.method_calls) == 2
assert mock_pub.method_calls[0][1][0] == 'openWB/chargepoint/5/get/voltages'
assert mock_pub.method_calls[1][1][0] == 'openWB/optional/int_display/theme'
+
+
+@pytest.mark.parametrize("index_test_template, expected_index", [
+ pytest.param(0, [2, 1], id="IDs korrekt"),
+ pytest.param(1, [0, 3], id="IDs gleich"),
+ pytest.param(2, [], id="keine Pläne"),
+])
+def test_upgrade_datastore_94(index_test_template, expected_index):
+ update_con = UpdateConfig()
+ update_con.all_received_topics = {"openWB/command/max_id/charge_template_scheduled_plan": 2}
+ with open(Path(__file__).resolve().parents[0]/"upgrade_datastore_94.json", "r") as f:
+ test_data = f.read()
+ update_con.all_received_topics.update(json.loads(test_data)[index_test_template])
+
+ update_con.upgrade_datastore_94()
+
+ plan_ids = []
+ for plan in update_con.all_received_topics["openWB/vehicle/template/charge_template/0"]["chargemode"][
+ "scheduled_charging"]["plans"]:
+ plan_ids.append(plan["id"])
+ assert plan_ids == expected_index
diff --git a/packages/helpermodules/upgrade_datastore_94.json b/packages/helpermodules/upgrade_datastore_94.json
new file mode 100644
index 0000000000..11a3cd1c85
--- /dev/null
+++ b/packages/helpermodules/upgrade_datastore_94.json
@@ -0,0 +1,3 @@
+[{"openWB/vehicle/template/charge_template/0": {"name": "Standard-Lade-Profil", "prio": false, "load_default": false, "time_charging": {"active": false, "plans": []}, "chargemode": {"selected": "stop", "pv_charging": {"dc_min_current": 145, "dc_min_soc_current": 145, "min_soc_current": 10, "min_current": 0, "feed_in_limit": false, "min_soc": 0, "phases_to_use": 0, "phases_to_use_min_soc": 3, "limit": {"selected": "soc", "amount": 1000, "soc": 100}}, "scheduled_charging": {"plans": [{"active": true, "frequency": {"selected": "daily", "once": "2021-11-01", "weekly": [false, false, false, false, false, false, false]}, "current": 14, "dc_current": 145, "id": 2, "name": "neuer Zielladen-Plan", "limit": {"selected": "amount", "amount": 1000, "soc_limit": 90, "soc_scheduled": 80}, "time": "07: 00", "phases_to_use": 0, "phases_to_use_pv": 0, "et_active": false, "bidi_power": 10000, "bidi_charging_enabled": false}, {"active": true, "frequency": {"selected": "daily", "once": "2021-11-01", "weekly": [false, false, false, false, false, false, false]}, "current": 14, "dc_current": 145, "id": 1, "name": "neuer Zielladen-Plan", "limit": {"selected": "amount", "amount": 1000, "soc_limit": 90, "soc_scheduled": 80}, "time": "07:00", "phases_to_use": 0, "phases_to_use_pv": 0, "et_active": false, "bidi_power": 10000, "bidi_charging_enabled": false}]}, "instant_charging": {"current": 10, "dc_current": 145, "limit": {"selected": "none", "amount": 1000, "soc": 50}, "phases_to_use": 3}, "eco_charging": {"current": 6, "dc_current": 145, "limit": {"selected": "none", "amount": 1000, "soc": 50}, "max_price": 0.0002, "phases_to_use": 3}}, "id": 0}},
+{"openWB/vehicle/template/charge_template/0": {"name": "Standard-Lade-Profil", "prio": false, "load_default": false, "time_charging": {"active": false, "plans": []}, "chargemode": {"selected": "stop", "pv_charging": {"dc_min_current": 145, "dc_min_soc_current": 145, "min_soc_current": 10, "min_current": 0, "feed_in_limit": false, "min_soc": 0, "phases_to_use": 0, "phases_to_use_min_soc": 3, "limit": {"selected": "soc", "amount": 1000, "soc": 100}}, "scheduled_charging": {"plans": [{"active": true, "frequency": {"selected": "daily", "once": "2021-11-01", "weekly": [false, false, false, false, false, false, false]}, "current": 14, "dc_current": 145, "id": 0, "name": "neuer Zielladen-Plan", "limit": {"selected": "amount", "amount": 1000, "soc_limit": 90, "soc_scheduled": 80}, "time": "07: 00", "phases_to_use": 0, "phases_to_use_pv": 0, "et_active": false, "bidi_power": 10000, "bidi_charging_enabled": false}, {"active": true, "frequency": {"selected": "daily", "once": "2021-11-01", "weekly": [false, false, false, false, false, false, false]}, "current": 14, "dc_current": 145, "id": 0, "name": "neuer Zielladen-Plan", "limit": {"selected": "amount", "amount": 1000, "soc_limit": 90, "soc_scheduled": 80}, "time": "07:00", "phases_to_use": 0, "phases_to_use_pv": 0, "et_active": false, "bidi_power": 10000, "bidi_charging_enabled": false}]}, "instant_charging": {"current": 10, "dc_current": 145, "limit": {"selected": "none", "amount": 1000, "soc": 50}, "phases_to_use": 3}, "eco_charging": {"current": 6, "dc_current": 145, "limit": {"selected": "none", "amount": 1000, "soc": 50}, "max_price": 0.0002, "phases_to_use": 3}}, "id": 0}},
+{"openWB/vehicle/template/charge_template/0": {"name": "Standard-Lade-Profil", "prio": false, "load_default": false, "time_charging": {"active": false, "plans": []}, "chargemode": {"selected": "stop", "pv_charging": {"dc_min_current": 145, "dc_min_soc_current": 145, "min_soc_current": 10, "min_current": 0, "feed_in_limit": false, "min_soc": 0, "phases_to_use": 0, "phases_to_use_min_soc": 3, "limit": {"selected": "soc", "amount": 1000, "soc": 100}}, "scheduled_charging": {"plans": []}, "instant_charging": {"current": 10, "dc_current": 145, "limit": {"selected": "none", "amount": 1000, "soc": 50}, "phases_to_use": 3}, "eco_charging": {"current": 6, "dc_current": 145, "limit": {"selected": "none", "amount": 1000, "soc": 50}, "max_price": 0.0002, "phases_to_use": 3}}, "id": 0}}]
\ No newline at end of file
diff --git a/packages/helpermodules/utils/error_handling.py b/packages/helpermodules/utils/error_handling.py
index 56e2a29bad..ee2e2810f8 100644
--- a/packages/helpermodules/utils/error_handling.py
+++ b/packages/helpermodules/utils/error_handling.py
@@ -11,6 +11,14 @@
CP_ERROR = ("Anhaltender Fehler beim Auslesen des Ladepunkts. Soll-Stromstärke, Lade- und Stecker-Status wird "
"zurückgesetzt.")
+INTERNAL_ERROR_HINT = ("Liebe Kunden, das Log ist zur Auswertung durch Support-Mitarbeiter der openWB GmbH gedacht. "
+ "Meldungen, die hier erscheinen können wie Fehlermeldungen aussehen, sind aber oft ganz normal. "
+ "Beispielsweise führen wir Abfragen der internen Hardware mehrere tausend mal pro Stunde aus "
+ "(wir gehen bis ans Limit der seriellen Kommunikation um eine möglichst feine Auflösung zu "
+ "erreichen), eine Abfrage-Fehlerquote von 1-2% ist dabei normal. Wirklich relevante "
+ "Fehlermeldungen erscheinen in der grafischen Nutzeroberfläche an prominenter Stelle. Bitte "
+ "belastet unseren Support nicht mit Fragen nach euch unbekannten Log-Meldungen.")
+
class ErrorTimerContext:
def __init__(self, topic: str, exceeded_msg: str, timeout: int = 60, hide_exception: bool = False):
@@ -28,12 +36,14 @@ def __exit__(self, exception_type, exception, exception_traceback) -> bool:
if self.error_timestamp is None:
self.error_timestamp = timecheck.create_timestamp()
Pub().pub(self.topic, self.error_timestamp)
- log.error(exception)
if (self.hide_exception is False or
timecheck.check_timestamp(self.error_timestamp, self.timeout + 10) is False):
# Fehlermeldung als abgelaufen markieren, bevor die Exception gesetzt wird, mit der Exception werden
# keine Werte mehr gepublished.
return False
+ else:
+ log.error(f"{exception}\n{INTERNAL_ERROR_HINT}")
+ return True
return True
def error_counter_exceeded(self) -> bool:
diff --git a/packages/main.py b/packages/main.py
index 25fd3ca184..2fbd15b490 100755
--- a/packages/main.py
+++ b/packages/main.py
@@ -233,6 +233,11 @@ def handler_midnight(self):
@__with_handler_lock(error_threshold=60)
def handler_random_nightly(self):
+ log.warning("Display wird neu geladen.") # nur zur Info im Log
+ # chromium neu starten, um größere Auswirkungen eines Speicherlecks zu vermeiden
+ run_command.run_command([
+ str(Path(__file__).resolve().parents[1] / "runs" / "update_local_display.sh"), "1"
+ ], process_exception=True)
try:
data.data.system_data["system"].thread_backup_and_send_to_cloud()
except Exception:
diff --git a/packages/modules/common/hardware_check.py b/packages/modules/common/hardware_check.py
index b43d9200e6..01abc892da 100644
--- a/packages/modules/common/hardware_check.py
+++ b/packages/modules/common/hardware_check.py
@@ -20,8 +20,7 @@
METER_NO_SERIAL_NUMBER = ("Die Seriennummer des Zählers für das Ladelog kann nicht ausgelesen werden. Wenn Sie die "
"Seriennummer für Abrechnungszwecke benötigen, wenden Sie sich bitte an unseren Support. Die "
"Funktionalität wird dadurch nicht beeinträchtigt!")
-EVSE_BROKEN = ("Auslesen der EVSE nicht möglich. Vermutlich ist die EVSE defekt oder hat eine unbekannte Modbus-ID. "
- "(Fehlermeldung nur relevant, wenn diese auf der Startseite oder im Status angezeigt wird.)")
+EVSE_BROKEN = "Auslesen der EVSE nicht möglich. Vermutlich ist die EVSE defekt oder hat eine unbekannte Modbus-ID. "
def check_meter_values(counter_state: CounterState, fault_state: Optional[FaultState] = None) -> None:
diff --git a/packages/modules/common/simcount/_simcounter.py b/packages/modules/common/simcount/_simcounter.py
index d4dbd83069..956a131123 100644
--- a/packages/modules/common/simcount/_simcounter.py
+++ b/packages/modules/common/simcount/_simcounter.py
@@ -10,7 +10,9 @@ def __init__(self, device_id: int, component_id: int, prefix: str):
self.prefix = "pv2" if prefix == "pv" and component_id != 1 else prefix
self.data: Optional[SimCounterState] = None
- def sim_count(self, power: float) -> Tuple[float, float]:
+ def sim_count(self, power: float, dc_power: Optional[float] = None) -> Tuple[float, float]:
+ if (self.prefix == "pv" or self.prefix == "pv2") and dc_power is not None and dc_power == 0:
+ power = 0
self.data = sim_count(power, self.topic, self.data, self.prefix)
return self.data.imported, self.data.exported
diff --git a/packages/modules/common/store/_chargepoint_internal.py b/packages/modules/common/store/_chargepoint_internal.py
index d16e821742..b4f3cc5560 100644
--- a/packages/modules/common/store/_chargepoint_internal.py
+++ b/packages/modules/common/store/_chargepoint_internal.py
@@ -24,7 +24,7 @@ def update(self):
pub_to_broker(f"{topic_prefix}/phases_in_use", self.state.phases_in_use, 2)
pub_to_broker(f"{topic_prefix}/charge_state", self.state.charge_state, 2)
pub_to_broker(f"{topic_prefix}/plug_state", self.state.plug_state, 2)
- pub_to_broker(f"{topic_prefix}/rfid", self.state.rfid)
+ pub_to_broker(f"{topic_prefix}/vehicle_id", self.state.vehicle_id)
pub_to_broker(f"{topic_prefix}/serial_number", self.state.serial_number)
pub_to_broker(f"{topic_prefix}/evse_current", self.state.evse_current, 2)
pub_to_broker(f"{topic_prefix}/max_evse_current", self.state.max_evse_current, 2)
@@ -32,11 +32,11 @@ def update(self):
pub_to_broker(f"{topic_prefix}/current_branch", self.state.current_branch)
pub_to_broker(f"{topic_prefix}/current_commit", self.state.current_commit)
if self.state.soc is not None:
- pub_to_broker(f"{topic_prefix}/get/soc", self.state.soc)
+ pub_to_broker(f"{topic_prefix}/soc", self.state.soc)
if self.state.soc_timestamp is not None:
pub_to_broker(f"{topic_prefix}/soc_timestamp", self.state.soc_timestamp)
if self.state.rfid_timestamp is not None:
- pub_to_broker(f"{topic_prefix}/vehicle_id", self.state.vehicle_id)
+ pub_to_broker(f"{topic_prefix}/rfid", self.state.rfid)
pub_to_broker(f"{topic_prefix}/rfid_timestamp", self.state.rfid_timestamp)
diff --git a/packages/modules/devices/discovergy/discovergy/api.py b/packages/modules/devices/discovergy/discovergy/api.py
index 978f3565bb..1dd6dcc5b0 100644
--- a/packages/modules/devices/discovergy/discovergy/api.py
+++ b/packages/modules/devices/discovergy/discovergy/api.py
@@ -5,7 +5,7 @@
def get_last_reading(session: Session, meter_id: str):
values = session.get(
- "https://api.discovergy.com/public/v1/last_reading",
+ "https://api.inexogy.com/public/v1/last_reading",
params={"meterId": meter_id},
timeout=3
).json()["values"]
diff --git a/packages/modules/devices/discovergy/discovergy/api_test.py b/packages/modules/devices/discovergy/discovergy/api_test.py
index 881f0ffd94..8fd2452d2b 100644
--- a/packages/modules/devices/discovergy/discovergy/api_test.py
+++ b/packages/modules/devices/discovergy/discovergy/api_test.py
@@ -71,7 +71,7 @@
@pytest.fixture
def mock_discovery_response(requests_mock):
def do_mock(json_str: str):
- requests_mock.get("https://api.discovergy.com/public/v1/last_reading?meterId=someMeterId", text=json_str)
+ requests_mock.get("https://api.inexogy.com/public/v1/last_reading?meterId=someMeterId", text=json_str)
return do_mock
diff --git a/packages/modules/devices/nibe/nibe/counter.py b/packages/modules/devices/nibe/nibe/counter.py
index 1c16e00b51..8fc69323a9 100644
--- a/packages/modules/devices/nibe/nibe/counter.py
+++ b/packages/modules/devices/nibe/nibe/counter.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python3
from typing import TypedDict, Any
+from pymodbus.constants import Endian
+
from modules.common.abstract_device import AbstractCounter
from modules.common.component_state import CounterState
from modules.common.component_type import ComponentDescriptor
@@ -29,7 +31,7 @@ def initialize(self) -> None:
def update(self):
unit = self.component_config.configuration.modbus_id
- power = self.client.read_input_registers(2166, ModbusDataType.UINT_32, unit=unit) / 10
+ power = self.client.read_input_registers(2166, ModbusDataType.UINT_32, wordorder=Endian.Little, unit=unit)
imported, exported = self.sim_counter.sim_count(power)
counter_state = CounterState(
diff --git a/packages/modules/devices/sungrow/sungrow/device.py b/packages/modules/devices/sungrow/sungrow/device.py
index b9becf32a5..69e5ed1e3a 100644
--- a/packages/modules/devices/sungrow/sungrow/device.py
+++ b/packages/modules/devices/sungrow/sungrow/device.py
@@ -29,6 +29,7 @@ def create_inverter_component(component_config: SungrowInverterSetup):
return SungrowInverter(component_config, device_config=device_config, client=client)
def update_components(components: Iterable[Union[SungrowBat, SungrowCounter, SungrowInverter]]):
+ pv_power = 0
nonlocal client
with client:
for component in components:
diff --git a/packages/modules/devices/sungrow/sungrow/inverter.py b/packages/modules/devices/sungrow/sungrow/inverter.py
index a934e04315..db2d5a3807 100644
--- a/packages/modules/devices/sungrow/sungrow/inverter.py
+++ b/packages/modules/devices/sungrow/sungrow/inverter.py
@@ -50,7 +50,7 @@ def update(self) -> float:
currents = self.__tcp_client.read_input_registers(5021, [ModbusDataType.INT_16]*3, unit=unit)
currents = [value * -0.1 for value in currents]
- imported, exported = self.sim_counter.sim_count(power)
+ imported, exported = self.sim_counter.sim_count(power, dc_power)
inverter_state = InverterState(
power=power,
diff --git a/packages/modules/internal_chargepoint_handler/chargepoint_module.py b/packages/modules/internal_chargepoint_handler/chargepoint_module.py
index 74eaf00d46..8b34b021d0 100644
--- a/packages/modules/internal_chargepoint_handler/chargepoint_module.py
+++ b/packages/modules/internal_chargepoint_handler/chargepoint_module.py
@@ -1,9 +1,12 @@
import logging
import time
+from helpermodules.broker import BrokerClient
from helpermodules.logger import ModifyLoglevelContext
+from helpermodules.utils import run_command
from helpermodules.utils.error_handling import CP_ERROR, ErrorTimerContext
+from helpermodules.utils.topic_parser import decode_payload
from modules.common.abstract_chargepoint import AbstractChargepoint
from modules.common.component_context import SingleComponentUpdateContext
from modules.common.component_state import ChargepointState
@@ -42,7 +45,6 @@ def __init__(self, local_charge_point_num: int,
hide_exception=True)
self.client_error_context.error_timestamp = internal_cp.get.error_timestamp
self.old_plug_state = False
- self.old_phases_in_use = 0
self.old_chargepoint_state = ChargepointState(plug_state=False,
charge_state=False,
imported=None,
@@ -65,6 +67,20 @@ def __init__(self, local_charge_point_num: int,
self.current_branch = SubData.system_data["system"].data["current_branch"]
self.current_commit = SubData.system_data["system"].data["current_commit"]
+ if float(run_command.run_command(["cat", "/proc/uptime"]).split(" ")[0]) < 180:
+ self.perform_phase_switch(1, 4)
+ self.old_phases_in_use = 1
+ else:
+ def on_connect(client, userdata, flags, rc):
+ client.subscribe(f"openWB/internal_chargepoint/{self.local_charge_point_num}/get/phases_in_use")
+
+ def on_message(client, userdata, message):
+ self.old_phases_in_use = decode_payload(message.payload)
+
+ self.old_phases_in_use = None
+ BrokerClient(f"subscribeInternalCp{self.local_charge_point_num}",
+ on_connect, on_message).start_finite_loop()
+
def set_current(self, current: float) -> None:
with SingleComponentUpdateContext(self.fault_state, update_always=False):
formatted_current = round(current*100) if self._precise_current else round(current)
diff --git a/packages/modules/web_themes/colors/source/src/components/mqttViewer/mqttClient.ts b/packages/modules/web_themes/colors/source/src/components/mqttViewer/mqttClient.ts
index 6113a764f4..16f5819884 100755
--- a/packages/modules/web_themes/colors/source/src/components/mqttViewer/mqttClient.ts
+++ b/packages/modules/web_themes/colors/source/src/components/mqttViewer/mqttClient.ts
@@ -10,7 +10,7 @@ import { type QoS } from 'mqtt-packet'
const defaultQoS: QoS = 0
const mqttConnection = {
host: location.hostname,
- port: location.protocol == 'https:' ? 443 : 80,
+ port: parseInt(location.port) || (location.protocol == 'https:' ? 443 : 80),
endpoint: '/ws',
protocol: (location.protocol == 'https:' ? 'wss' : 'ws') as MqttProtocol,
clean: true,
@@ -20,8 +20,6 @@ const mqttConnection = {
.toString(36)
.replace(/[^a-z]+/g, '')
.substring(0, 6),
- username: 'openWB',
- password: 'openWB',
}
const subscription = {
topic: '',
diff --git a/packages/modules/web_themes/koala/source/src/components/BaseCarousel.vue b/packages/modules/web_themes/koala/source/src/components/BaseCarousel.vue
index e8dce0cbe8..15659bf6e7 100644
--- a/packages/modules/web_themes/koala/source/src/components/BaseCarousel.vue
+++ b/packages/modules/web_themes/koala/source/src/components/BaseCarousel.vue
@@ -34,7 +34,14 @@
-