Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
588dd49
check for patterns
ndrsnhs Aug 12, 2025
a0536a3
Bump @babel/runtime in /packages/modules/web_themes/colors/source
dependabot[bot] Aug 19, 2025
071632b
updates
benderl Aug 19, 2025
8ab744e
build UI (#2681)
LKuemmel Aug 19, 2025
ca8bc59
show CURRENT_OUT_OF_NOMINAL_DIFFERENCE in cp info box (#2682)
LKuemmel Aug 19, 2025
5e3aefd
Merge pull request #2678 from openWB/dependabot/npm_and_yarn/packages…
benderl Aug 19, 2025
c11aaf7
Update energycharts/tariff.py - add retry on timeout (#2684)
tpd-opitz Aug 21, 2025
629c5d4
Merge pull request #2680 from benderl/cards
benderl Aug 21, 2025
56e1692
fix deprecated meta tag
benderl Aug 19, 2025
21f389d
optimize group size calculation
benderl Aug 21, 2025
4d8f895
Merge pull request #2679 from benderl/koala
benderl Aug 21, 2025
1d9ecec
Build Web Theme: Koala
benderl Aug 21, 2025
7445f2b
Scheduled charging (#2688)
LKuemmel Aug 21, 2025
42a6b9d
build UI (#2690)
LKuemmel Aug 21, 2025
300d956
set max value for phase_switch_delay to 60 (#2689)
LKuemmel Aug 21, 2025
af95487
fix cp soc update (#2691)
LKuemmel Aug 21, 2025
dc84cc5
sonnenbatterie: fix setting default mode
benderl Aug 22, 2025
a9e02b2
fix initialization of power_limit
benderl Aug 22, 2025
ae4203b
bmwbc: fix initial login
rleidner Aug 22, 2025
3dff064
Merge pull request #2693 from rleidner/soc_bmwbc_p8
benderl Aug 22, 2025
20d3363
check hardware pahses vs chargemode phases (#2694)
LKuemmel Aug 22, 2025
c8b5a99
Colors theme fixes (#2696)
cshagen Aug 25, 2025
c02c6cc
Build Web Theme: Colors
benderl Aug 25, 2025
f3a42bd
Merge pull request #2692 from benderl/speichersteuerung
benderl Aug 25, 2025
b65cb10
Bugfix Smarthome Lambda Wärmepumpe Modbus Kommunikation (#2695)
route662 Aug 25, 2025
2201b6a
Fix command (#2685)
LKuemmel Aug 25, 2025
c9f5d19
Koala modifications (#2699)
benderl Aug 25, 2025
2cbdade
Build Web Theme: Koala
benderl Aug 25, 2025
5c9e67f
refactor BaseCarousel
benderl Aug 25, 2025
f0532c4
Merge pull request #2700 from benderl/koala
benderl Aug 25, 2025
198ed46
Build Web Theme: Koala
benderl Aug 25, 2025
8ef225a
fix b23 and mpm3pm (#2701)
LKuemmel Aug 26, 2025
dcc24fa
fix typo in key (#2703)
ndrsnhs Aug 27, 2025
cc002ba
fix valid data for evse current (#2704)
LKuemmel Aug 27, 2025
b0a43c0
add more info sections (#2707)
benderl Aug 28, 2025
e409a34
limit log file size of thread_errors.log (#2697)
LKuemmel Aug 28, 2025
b4cea3d
koala cleanup
benderl Aug 28, 2025
e2ee6cf
Merge pull request #2710 from benderl/koala
benderl Aug 28, 2025
0665e11
Build Web Theme: Koala
benderl Aug 28, 2025
19f2b3d
build settings
benderl Aug 28, 2025
d4aa282
Merge pull request #2711 from benderl/build-ui
benderl Aug 28, 2025
bf3759a
command: fix copying charge point template
benderl Aug 28, 2025
af0b2c8
Merge pull request #2654 from ndrsnhs/chargepoint-identification-with…
benderl Aug 28, 2025
a5d8c11
Merge pull request #2712 from benderl/fixes
benderl Aug 28, 2025
c3e270b
fix pro error handling (#2708)
LKuemmel Aug 29, 2025
a96a29b
fix typo (#2714)
LKuemmel Aug 29, 2025
ff20159
Fix zero currents / zero power (#2705)
seaspotter Aug 29, 2025
5ba3c5f
Sungrow Battery Readings improvements (#2713)
seaspotter Aug 29, 2025
e5e188a
build UI (#2715)
LKuemmel Aug 29, 2025
aa241f8
improve hardware check (#2702)
LKuemmel Aug 29, 2025
93b9e77
Sonnenbatterie bat fix (#2709)
ndrsnhs Aug 29, 2025
fb64ee4
Update version 2.1.8-Beta.2
ndrsnhs Aug 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion packages/control/bat.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get_factory() -> Get:

@dataclass
class Set:
power_limit: float = field(default=0, metadata={"topic": "set/power_limit"})
power_limit: float = field(default=None, metadata={"topic": "set/power_limit"})


def set_factory() -> Set:
Expand Down
4 changes: 2 additions & 2 deletions packages/control/bat_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ def get_power_limit(self):
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 Speichereistung begrenzen.
# 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
Expand All @@ -315,7 +315,7 @@ def get_power_limit(self):
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 Netzubezug lädt.")
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:
Expand Down
19 changes: 14 additions & 5 deletions packages/control/chargepoint/chargepoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from threading import Thread, Event
import traceback
from typing import Dict, Optional, Tuple
from fnmatch import fnmatch

from control.algorithm.utils import get_medium_charging_current
from control.chargelog import chargelog
Expand Down Expand Up @@ -154,11 +155,18 @@ def _is_autolock_inactive(self) -> Tuple[bool, Optional[str]]:
def _is_manual_lock_inactive(self) -> Tuple[bool, Optional[str]]:
# Die Pro schickt je nach Timing auch nach Abstecken noch ein paar Zyklen den Tag. Dann darf der Ladepunkt
# nicht wieder entsperrt werden.
if (self.data.get.rfid or
self.data.get.vehicle_id or
self.data.set.rfid) in self.template.data.valid_tags:
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:

# Prüfung auf ein passendes Muster
# Vergleiche werden case-insensitive durchgeführt
# das vereinfacht die Eingabe, kann aber auch für falsche Treffer sorgen.
# 'fnmatch()' ist case-insensitive
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:
Pub().pub(f"openWB/set/chargepoint/{self.num}/set/manual_lock", True)

if self.data.set.manual_lock:
Expand Down Expand Up @@ -561,6 +569,7 @@ def hw_bidi_capable(self) -> BidiState:

def set_phases(self, phases: int) -> int:
charging_ev = self.data.set.charging_ev_data
phases = min(phases, self.get_max_phase_hw())

if phases != self.data.get.phases_in_use:
# Wenn noch kein Eintrag im Protokoll erstellt wurde, wurde noch nicht geladen und die Phase kann noch
Expand Down
5 changes: 4 additions & 1 deletion packages/control/ev/ev.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,10 @@ def auto_phase_switch(self,
delay)[1])
control_parameter.state = ChargepointState.PHASE_SWITCH_DELAY
elif condition_msg:
log.debug(f"Keine Phasenumschaltung{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:
# Timer laufen lassen
Expand Down
35 changes: 11 additions & 24 deletions packages/helpermodules/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,15 @@ def _get_max_ids(self) -> None:
max_id = default
for topic, payload in received_topics.items():
if re.search(topic_str, topic) is not None:
try:
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)
except KeyError:
# überspringe Profile, die keinen Eintrag für Pläne haben.
# Da gab es einen Bug beim Kopieren.
pass
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"]:
Expand Down Expand Up @@ -261,15 +256,7 @@ def setup_added_chargepoint():
Pub().pub(f'openWB/chargepoint/{new_id}/set/manual_lock', False)
{Pub().pub(f"openWB/chargepoint/{new_id}/get/"+k, v) for (k, v) in asdict(chargepoint.Get()).items()}
charge_template = SubData.ev_charge_template_data[f"ct{SubData.ev_data['ev0'].data.charge_template}"]
for time_plan in charge_template.data.time_charging.plans:
Pub().pub(f'openWB/chargepoint/{new_id}/set/charge_template/time_charging/plans',
dataclass_utils.asdict(time_plan))
for scheduled_plan in charge_template.data.chargemode.scheduled_charging.plans:
Pub().pub(f'openWB/chargepoint/{new_id}/set/charge_template/chargemode/scheduled_charging/plans',
scheduled_plan)
charge_template = dataclass_utils.asdict(charge_template.data)
charge_template["chargemode"]["scheduled_charging"]["plans"].clear()
charge_template["time_charging"]["plans"].clear()
Pub().pub(f'openWB/chargepoint/{new_id}/set/charge_template', charge_template)
self.max_id_hierarchy = self.max_id_hierarchy + 1
Pub().pub("openWB/set/command/max_id/hierarchy", self.max_id_hierarchy)
Expand Down Expand Up @@ -352,7 +339,7 @@ def removeChargepoint(self, connection_id: str, payload: dict) -> None:
""" löscht ein Ladepunkt.
"""
if self.max_id_hierarchy < payload["data"]["id"]:
log.error(
pub_user_message(
payload, connection_id,
f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene '
f'ID \'{self.max_id_hierarchy}\'.', MessageType.ERROR)
Expand All @@ -379,7 +366,7 @@ def addChargepointTemplate(self, connection_id: str, payload: dict) -> None:
self.max_id_chargepoint_template)
# if copying a template, copy autolock plans
if "data" in payload and "copy" in payload["data"]:
for _, plan in data.data.cp_template_data[f'cpt{payload["data"]["copy"]}'].data.autolock.plans.items():
for plan in data.data.cp_template_data[f'cpt{payload["data"]["copy"]}'].data.autolock.plans:
new_plan = asdict(plan).copy()
new_plan["id"] = self.max_id_autolock_plan + 1
Pub().pub(f'openWB/set/chargepoint/template/{new_id}/autolock/{new_plan["id"]}', new_plan)
Expand Down
2 changes: 1 addition & 1 deletion packages/helpermodules/create_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def config_and_state():
"--| Counter_Max_Currents: "
f"{component_data.data.config.max_currents}\n")
else:
parsed_data += ("--| Counter_Type: Sonstiger Zähler\nCOUNTER_MAX_CURRENTS: "
parsed_data += ("--| Counter_Type: Sonstiger Zähler\n--| Counter_Max_Currents: "
f"{component_data.data.config.max_currents}\n")
parsed_data += (f"--| Counter_Power: {component_data.data.get.power/1000}kW\n"
f"--| Counter_Currents: {component_data.data.get.currents}A\n"
Expand Down
12 changes: 9 additions & 3 deletions packages/helpermodules/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,12 +334,15 @@ def mb_to_bytes(megabytes: int) -> int:
logging.getLogger("uModbus").setLevel(logging.WARNING)
logging.getLogger("websockets").setLevel(logging.WARNING)

thread_errors_path = Path(Path(__file__).resolve().parents[2]/"ramdisk"/"thread_errors.log")
thread_errors_path = Path(RAMDISK_PATH+"thread_errors.log")
with thread_errors_path.open("w") as f:
f.write("")

def threading_excepthook(args):
with open(RAMDISK_PATH+"thread_errors.log", "a") as f:
if thread_errors_path.exists() and thread_errors_path.stat().st_size > 10_000:
with thread_errors_path.open("w") as f:
f.write("")
with open(thread_errors_path, "a") as f:
f.write("Uncaught exception in thread:\n")
f.write(f"Type: {args.exc_type}\n")
f.write(f"Value: {args.exc_value}\n")
Expand All @@ -348,7 +351,10 @@ def threading_excepthook(args):
threading.excepthook = threading_excepthook

def handle_unhandled_exception(exc_type, exc_value, exc_traceback):
with open(RAMDISK_PATH+"thread_errors.log", "a") as f:
if thread_errors_path.exists() and thread_errors_path.stat().st_size > 10_000:
with thread_errors_path.open("w") as f:
f.write("")
with open(thread_errors_path, "a") as f:
f.write("Uncaught exception:\n")
f.write(f"Type: {exc_type}\n")
f.write(f"Value: {exc_value}\n")
Expand Down
5 changes: 3 additions & 2 deletions packages/helpermodules/setdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,8 @@ def process_chargepoint_get_topics(self, msg):
self._validate_value(msg, int, [(0, 2)])
elif ("/get/evse_current" in msg.topic or
"/get/max_evse_current" in msg.topic):
self._validate_value(msg, float, [(float("-inf"), 0), (0, 0), (6, 32), (600, 3200)])
# AC-EVSE: 0, 6-32, 600-3200, DC-EVSE 0-500
self._validate_value(msg, float, [(0, 3200)])
elif ("/get/version" in msg.topic or
"/get/current_branch" in msg.topic or
"/get/current_commit" in msg.topic):
Expand Down Expand Up @@ -717,7 +718,7 @@ def process_general_topic(self, msg: mqtt.MQTTMessage):
elif "openWB/set/general/chargemode_config/pv_charging/switch_off_threshold" in msg.topic:
self._validate_value(msg, float)
elif "openWB/set/general/chargemode_config/phase_switch_delay" in msg.topic:
self._validate_value(msg, int, [(5, 20)])
self._validate_value(msg, int, [(5, 60)])
elif "openWB/set/general/chargemode_config/pv_charging/control_range" in msg.topic:
self._validate_value(msg, int, collection=list)
elif "openWB/set/general/chargemode_config/pv_charging/min_bat_soc" in msg.topic:
Expand Down
3 changes: 3 additions & 0 deletions packages/helpermodules/subdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg
if var["cp"+index].chargepoint.data.set.charging_ev > -1:
Pub().pub(f'openWB/set/vehicle/{var["cp"+index].chargepoint.data.set.charging_ev}'
'/get/force_soc_update', True)
elif var["cp"+index].chargepoint.data.set.charging_ev_prev > -1:
Pub().pub(f'openWB/set/vehicle/{var["cp"+index].chargepoint.data.set.charging_ev_prev}'
'/get/force_soc_update', True)
self.set_json_payload_class(var["cp"+index].chargepoint.data.get, msg)
elif (re.search("/chargepoint/[0-9]+/get/error_timestamp$", msg.topic) is not None and
hasattr(var[f"cp{index}"].chargepoint.chargepoint_module, "client_error_context")):
Expand Down
92 changes: 91 additions & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

class UpdateConfig:

DATASTORE_VERSION = 93
DATASTORE_VERSION = 94

valid_topic = [
"^openWB/bat/config/bat_control_permitted$",
Expand Down Expand Up @@ -2425,3 +2425,93 @@ def upgrade(topic: str, payload) -> Optional[dict]:
return {topic: payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 93)

def upgrade_datastore_93(self) -> None:
# Pläne die keinen plans Key haben, id=None
max_id = -1
none_id = False
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)
try:
for plan in payload["chargemode"]["scheduled_charging"]["plans"]:
try:
max_id = max(plan["id"], max_id)
except TypeError:
if plan["id"] is None:
none_id = True
else:
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)
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:
payload = decode_payload(payload)
for plan in payload["chargemode"]["scheduled_charging"]["plans"]:
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)

max_id = -1
none_id = False
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)
try:
for plan in payload["time_charging"]["plans"]:
try:
max_id = max(plan["id"], max_id)
except TypeError:
if plan["id"] is None:
none_id = True
else:
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)
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:
payload = decode_payload(payload)
for plan in payload["time_charging"]["plans"]:
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)

max_id = -1
none_id = False
for topic, payload in self.all_received_topics.items():
if re.search("openWB/chargepoint/template/[0-9]+$", topic) is not None:
payload = decode_payload(payload)
try:
for plan in payload["autolock"]["plans"]:
try:
max_id = max(plan["id"], max_id)
except TypeError:
if plan["id"] is None:
none_id = True
else:
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)
if none_id:
for topic, payload in self.all_received_topics.items():
if re.search("openWB/chargepoint/template/[0-9]+$", topic) is not None:
payload = decode_payload(payload)
for plan in payload["autolock"]["plans"]:
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)
self.__update_topic("openWB/system/datastore_version", 94)
Loading