Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f484e6e
dimm kit: improve error handling, handle deleted output devices (#3868)
LKuemmel Sep 18, 2026
68e51d2
PV-Reset: NO_CHARGING_ALLOWED blockiert den robustness-Reset nicht me…
seaspotter Sep 18, 2026
a585da2
fix: external_openwb chargepoint crashes when fault_state topic is mi…
seaspotter Sep 18, 2026
7fe82d1
consumer: fix hierarchy, default usage (#3975)
LKuemmel Sep 18, 2026
e4ae43c
fix: external_openwb occasionally missed retained MQTT topics under l…
seaspotter Sep 21, 2026
6124e34
Systembericht Verbraucher (#3956)
ndrsnhs Sep 21, 2026
31b3b58
fix: Ladepunkte nutzen die Abschaltschwelle der Verbraucher (#3984)
s0170071 Sep 21, 2026
5ebb434
removing meter-only consumers from load management priority (#3998)
LKuemmel Sep 23, 2026
29aa72b
build UI (#3999)
LKuemmel Sep 23, 2026
76bdbf7
Update: Hausverbrauch Zähler (#3991)
AlexanderHa98 Sep 23, 2026
2afb81d
avm: fix consumer switch_on/off using unresolved ain, fix bare return…
seaspotter Sep 23, 2026
2a2e92a
build UI (#4000)
LKuemmel Sep 23, 2026
89f2c5a
Feature negative dc charging (#3795)
AlexanderHa98 Sep 23, 2026
94ade8c
Build Themes
actions-user Sep 23, 2026
a205fd5
avm consumer: invertierte Session-Refresh-Bedingung beheben (#4001)
seaspotter Sep 23, 2026
83e4c0b
Peak Filter: bei Rückwärtssprüngen auf Wert größer/gleich Höchststand…
LKuemmel Sep 25, 2026
fe92600
build UI (#4008)
LKuemmel Sep 25, 2026
e5165c3
Consumer: Add Daikin, EGO and EMS-ESP (#3969)
seaspotter Sep 25, 2026
4298cff
Consumer: Add Mitsubishi and Gree AC modules (#3973)
seaspotter Sep 25, 2026
bf9c504
build UI (#4010)
LKuemmel Sep 25, 2026
714e747
Fixed Hours Tariff: migrate fixed-hours weekdays to Python weekday fo…
LKuemmel Sep 25, 2026
30be89c
Hierarchie: Entferne mit Verbraucher verknüpften Zähler (#4011)
LKuemmel Sep 28, 2026
606cf2b
Speicher-Vorrang bei Erholung von Mindest- bis Maximal-SoC (#3986)
seaspotter Sep 28, 2026
75812f0
build UI (#4025)
LKuemmel Sep 28, 2026
307a7b4
Add ACLs consumer and load management topics (#3964)
Brett-S-OWB Sep 28, 2026
a33f64c
fix colors theme: leftover pv_charging topic paths from PR #3812 topi…
LKuemmel Sep 28, 2026
828534c
Build Themes
actions-user Sep 28, 2026
12ed3d7
Consumer: Add Kermi, LG Therma V, Luxtronik (#3971)
seaspotter Sep 28, 2026
8975599
Sonnenbatterie: add OperationMode Optimization (#3769)
ndrsnhs Sep 28, 2026
42d2c46
Fix load management single phase netting and Bidi discharge currents …
seaspotter Sep 28, 2026
bfb48db
build UI (#4026)
LKuemmel Sep 28, 2026
417fb0c
Improve Monthly/Yearly Log (#3829)
AlexanderHa98 Sep 28, 2026
165b22a
Fix Tesla/Powerwall data handling and improve robustness of control l…
NicoP-codeing Sep 28, 2026
68fb41f
Error status table (#3943)
Brett-S-OWB Sep 29, 2026
c3149d2
Build Themes
actions-user Sep 29, 2026
da3c795
add consumer temperature readings to consumer card (#3974)
Brett-S-OWB Sep 29, 2026
564405c
Build Themes
actions-user Sep 29, 2026
82e691a
Consumer: Add M-TEC, Weishaupt, Xtherma (#3972)
seaspotter Sep 29, 2026
a795adf
Merge remote-tracking branch 'upstream/master' into feature_fix_charg…
AlexanderHa98 Sep 29, 2026
685a68e
Fix charge cost calc (#4022)
AlexanderHa98 Sep 29, 2026
c894e6b
fix circular error
AlexanderHa98 Sep 29, 2026
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
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ data/config/eebus/certs/*
data/log/*
data/charge_log/*
data/daily_log/*
data/daily_totals/*
data/monthly_log/*
data/monthly_totals/*
data/backup/*.tar.gz
data/backup/*.openwb-backup*
data/restore/*.tar.gz
data/data_migration/*.tar.gz
data/generate_totals.lock
ramdisk/*
web/lastcommit
!.gitignore
122 changes: 118 additions & 4 deletions data/config/mosquitto/public/default-dynamic-security.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"anonymousGroup": "anonymous",
"roles": [
{
"rolename": "openwb-version:9",
"rolename": "openwb-version:10",
"textname": "openWB Versionsnummer",
"textdescription": "Diese Rolle ist ein Platzhalter für die openWB Versionsnummer und wird automatisch aktualisiert. Sie hat keine direkten Berechtigungen.",
"acls": []
Expand Down Expand Up @@ -194,6 +194,12 @@
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/log_data_ready",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/usage_terms_acknowledged",
Expand Down Expand Up @@ -500,6 +506,12 @@
"topic": "openWB/system/security/access/Status",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/counter/get/loadmanagement_prios",
"priority": 0,
"allow": true
}
]
},
Expand Down Expand Up @@ -1686,6 +1698,18 @@
"topic": "openWB/chargepoint/+/config",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/+/module",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/+/extra_meter",
"priority": 0,
"allow": true
}
]
},
Expand Down Expand Up @@ -1799,19 +1823,73 @@
"acls": [
{
"acltype": "publishClientSend",
"topic": "openWB/consumer/+/config",
"topic": "openWB/set/command/+/todo/addConsumer",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/consumer/+/module",
"topic": "openWB/set/command/+/todo/removeConsumer",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/consumer/+/usage",
"topic": "openWB/set/command/+/todo/addConsumerSchedulePlan",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/command/+/todo/removeConsumerSchedulePlan",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/command/+/todo/addConsumerTimePlan",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/command/+/todo/removeConsumerTimePlan",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/consumer/+/config",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/consumer/+/module",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/consumer/+/usage",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/consumer/+/extra_meter",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/general/legacy_smarthome_active",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/security/access/ConsumerConfiguration",
"priority": 0,
"allow": true
},
Expand All @@ -1833,6 +1911,42 @@
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/+/extra_meter",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/extern",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/legacy_smarthome_active",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/counter/get/hierarchy",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/device/+/config",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/device/+/component/+/config",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/configurable/consumers",
Expand Down
142 changes: 121 additions & 21 deletions packages/control/algorithm/bidi_charging.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import logging
from control import data
from control.chargemode import Chargemode
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE
from control.algorithm.filter_chargepoints import filtered_loads_to_str, get_loads_by_chargemodes
from control.chargepoint.chargepoint import Chargepoint
from helpermodules.phase_handling import voltages_mean

from control.limiting_value import LoadmanagementLimit
from control.loadmanagement import Loadmanagement

import control.algorithm.common as common
from typing import List

log = logging.getLogger(__name__)


Expand All @@ -13,33 +20,126 @@ def __init__(self):
pass

def set_bidi(self):
"""Setzt den verfügbaren Strom für bidirektionales Laden und Entladen.
Dafür gibt es jeweils eine Grenze für das Laden und Entladen
(raw_currents_left und raw_exported_currents_left).
Entladen erhöht die verfügbare Grenze für das Laden, während Laden
die verfügbare Grenze für das Entladen erhöht.
Das Lastmanagement stellt sicher, dass die Summe der Ströme die am
Zähler verfügbare Leistung nicht überschreitet.
"""
grid_counter = data.data.counter_all_data.get_evu_counter()
log.debug(f"Nullpunktanpassung {grid_counter.data.set.surplus_power_left}W")
zero_point_adjustment = grid_counter

# CPs nach Modus filtern, dann verfügbare Ströme gegen alle relevanten Counter-Limits begrenzen.
# -> Um bei mehreren Bidi-CPs das Entladen bei der Nullpunktanpassung gleichmäßig zu verteilen
preferenced_cps = get_loads_by_chargemodes(CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE)
if preferenced_cps:
log.info(f"Verbraucher {filtered_loads_to_str(preferenced_cps)}")
while len(preferenced_cps):
cp: Chargepoint = preferenced_cps[0]
zero_point_adjustment = grid_counter.data.set.surplus_power_left / len(preferenced_cps)
log.debug(f"Nullpunktanpassung für LP{cp.num}: verbleibende Leistung {zero_point_adjustment}W")
missing_currents = [zero_point_adjustment / cp.data.get.phases_in_use /
230 for i in range(0, cp.data.get.phases_in_use)]

counts = self.get_counts(cp)

cp.data.set.target_current = 0

missing_currents = self.get_missing_currents(preferenced_cps, grid_counter)
log.debug(f"Bidi-LP{cp.num}: missing currents {missing_currents}A")

counters = data.data.counter_all_data.get_counters_to_check(cp.num)
for counter in counters:
available_currents, limit = Loadmanagement().get_available_currents_bidi(
missing_currents, voltages_mean(cp.data.get.voltages), data.data.counter_data[counter])

if limit.limiting_value is not None:
cp.data.control_parameter.limit = limit

available_for_cp = common.available_current_for_load(
cp, counts, available_currents, missing_currents, bidi_mode=True)

# Der neue Strom darf nicht höher als der bisher gesetzte Strom sein
current = common.get_current_to_set(
cp.data.set.current, available_for_cp, cp.data.set.target_current)

cp.data.set.current = current
log.info(f"LP{cp.num}: Stromstärke {current}A")

# Ausgabe LIMIT-MSG
self._set_loadmangement_message(current, limit, cp)

common.set_current_counterdiff(cp.data.set.target_current, current, cp, surplus=True)

preferenced_cps.pop(0)

def _set_loadmangement_message(self,
current: float,
limit: LoadmanagementLimit,
chargepoint: Chargepoint) -> None:
# Strom muss an diesem Zähler geändert werden
log.debug(
f"current {current} target {chargepoint.data.set.target_current} set current {chargepoint.data.set.current}"
f" required currents {chargepoint.data.control_parameter.required_currents}")
required_currents = chargepoint.data.control_parameter.required_currents
required_current = min(required_currents) if current < 0 else max(required_currents)
if (limit.message and
# Strom erreicht nicht die vorgegebene Stromstärke
round(current, 2) != round(required_current, 2)):
if current == 0:
chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen/entladen werden"
f"{limit.message}")
elif current < 0:
chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke entladen werden"
f"{limit.message}")
else:
chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden"
f"{limit.message}")

def get_counts(self, chargepoint: Chargepoint) -> List[int]:

counts = [0]*3
required_currents = chargepoint.data.control_parameter.required_currents
for i in range(3):
if required_currents[i] != 0:
counts[i] += 1
return counts

def get_missing_currents(self, preferenced_cps: List[Chargepoint], grid_counter) -> List[float]:
cp = preferenced_cps[0]
missing_currents = [0, 0, 0]
if cp.data.control_parameter.chargemode == Chargemode.INSTANT_CHARGING:
# Entladen im Instant-Charging-Modus

# cp.control_parameter.required_currents enthält duch self.surplus_controlled.set_required_current_to_max()
# in calc_current nur noch die maximalen Ströme
dc_current = cp.data.set.charging_ev_data.charge_template.data.chargemode.instant_charging.dc_current
if dc_current < 0:
# Phasen in use berücksichtigen
missing_currents = [dc_current for i in range(0, cp.data.get.phases_in_use)]
missing_currents += [0] * (3 - len(missing_currents))
if zero_point_adjustment > 0:
if cp.data.set.charging_ev_data.charge_template.bidi_charging_allowed(
cp.data.control_parameter.current_plan, cp.data.set.charging_ev_data.data.get.soc):
for index in range(0, 3):
missing_currents[index] = min(cp.data.control_parameter.required_current,
missing_currents[index])
else:
log.info(f"LP{cp.num}: Nur bidirektional entladen erlaubt, da SoC-Limit erreicht.")
missing_currents = [0, 0, 0]
else:

for index in range(0, 3):
missing_currents[index] = cp.check_min_max_current(
missing_currents[index], cp.data.get.phases_in_use)

else:
# Default Nullpunktanpassung
zero_point_adjustment = grid_counter.data.set.surplus_power_left / len(preferenced_cps)
log.debug(f"Nullpunktanpassung für LP{cp.num}: verbleibende Leistung {zero_point_adjustment}W")
missing_currents = [zero_point_adjustment / cp.data.get.phases_in_use /
230 for i in range(0, cp.data.get.phases_in_use)]
missing_currents += [0] * (3 - len(missing_currents))
if zero_point_adjustment > 0:
if cp.data.set.charging_ev_data.charge_template.bidi_charging_allowed(
cp.data.control_parameter.current_plan, cp.data.set.charging_ev_data.data.get.soc):
for index in range(0, 3):
missing_currents[index] = cp.check_min_max_current(missing_currents[index],
cp.data.get.phases_in_use)
grid_counter.update_surplus_values_left(missing_currents, voltages_mean(cp.data.get.voltages))
cp.data.set.current = missing_currents[0]
log.info(f"LP{cp.num}: Stromstärke {missing_currents}A")
preferenced_cps.pop(0)
missing_currents[index] = min(cp.data.control_parameter.required_current,
missing_currents[index])
else:
log.info(f"LP{cp.num}: Nur bidirektional entladen erlaubt, da SoC-Limit erreicht.")
missing_currents = [0, 0, 0]
else:
for index in range(0, 3):
missing_currents[index] = cp.check_min_max_current(missing_currents[index],
cp.data.get.phases_in_use)

return missing_currents
27 changes: 14 additions & 13 deletions packages/control/algorithm/chargemodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@

# Lademodi in absteigender Priorität
# Tupel-Inhalt:(eingestellter Modus, tatsächlich genutzter Modus, Priorität)
CHARGEMODES = ((Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING),
(None, Chargemode.TIME_CHARGING),
(Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING),
(Chargemode.ECO_CHARGING, Chargemode.INSTANT_CHARGING),
(Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING),
(Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING),
(Chargemode.ECO_CHARGING, Chargemode.PV_CHARGING),
(Chargemode.PV_CHARGING, Chargemode.PV_CHARGING),
CHARGEMODES = ((Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING), # 0
(None, Chargemode.TIME_CHARGING), # 1
(Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING), # 2
(Chargemode.ECO_CHARGING, Chargemode.INSTANT_CHARGING), # 3
(Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING), # 4
(Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING), # 5
(Chargemode.ECO_CHARGING, Chargemode.PV_CHARGING), # 6
(Chargemode.PV_CHARGING, Chargemode.PV_CHARGING), # 7
# niedrigere Priorität soll nachrangig geladen, aber zuerst entladen werden
(Chargemode.SCHEDULED_CHARGING, Chargemode.BIDI_CHARGING),
(None, Chargemode.STOP),)
(Chargemode.INSTANT_CHARGING, Chargemode.BIDI_CHARGING), # 8
(Chargemode.SCHEDULED_CHARGING, Chargemode.BIDI_CHARGING), # 9
(None, Chargemode.STOP),) # 10

CONSIDERED_CHARGE_MODES_SURPLUS = (CHARGEMODES[0], *CHARGEMODES[3:8])
CONSIDERED_CHARGE_MODES_PV_ONLY = CHARGEMODES[5:8]
CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT = CHARGEMODES[0:5]
CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-2]
CONSIDERED_CHARGE_MODES_NO_CURRENT = (CHARGEMODES[9],)
CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE = (CHARGEMODES[8],)
CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-3]
CONSIDERED_CHARGE_MODES_NO_CURRENT = (CHARGEMODES[10],)
CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE = (CHARGEMODES[8:10])
CONSIDERED_CHARGE_MODES_CHARGING = CHARGEMODES[0:8]
Loading
Loading