Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e6e6091
draft
LKuemmel Jun 16, 2026
d6bb6e0
draft
LKuemmel Jun 16, 2026
d751d58
fixes
LKuemmel Jun 17, 2026
f7cb8f7
fixes
LKuemmel Jun 18, 2026
f6fda77
Anlauferkennung, Betriebsmodus zurücksetzen, Befehle für Pläne (#3588)
LKuemmel Jun 24, 2026
582addf
on time, fixes (#3612)
LKuemmel Jun 30, 2026
081a91c
on_time accessible via 1883 (#3626)
LKuemmel Jul 3, 2026
60be48f
Add Consumer tab and cards to koala web theme (#3630)
Brett-S-OWB Jul 6, 2026
7b4628d
imported and exported for consumers; daily, monthly log; daily yields…
LKuemmel Jul 8, 2026
d46f105
Consumer cards in carousel - small screen mobile view (#3637)
Brett-S-OWB Jul 17, 2026
7ea654f
Intergrate consumers in koala theme charts (#3672)
Brett-S-OWB Jul 20, 2026
4720fe9
Add consumer component to display flow chart (#3673)
Brett-S-OWB Jul 21, 2026
476f711
energy percentage, set usage per json, several fixes (#3705)
LKuemmel Jul 24, 2026
0f24a0c
improve wait for start
LKuemmel Jul 24, 2026
b1c863c
shelly, pytest (#3730)
LKuemmel Jul 29, 2026
f253bd9
fix colors, lookup dict names and colors for consumers (#3736)
LKuemmel Jul 30, 2026
4a05263
hide mode controls for consumer measurement only (#3715)
Brett-S-OWB Aug 3, 2026
d330f0a
update consumer-configuration-access (user management) (#3743)
Brett-S-OWB Aug 4, 2026
f27d575
diagram colors, pytests, remove extra meters from log data (#3742)
LKuemmel Aug 5, 2026
fa73a09
ammend
LKuemmel Aug 6, 2026
40d6f90
fix_merge
LKuemmel Aug 7, 2026
ec9e286
switch mode on card
LKuemmel Jul 31, 2026
4e1ad3c
update consumer button groups
Brett-S-OWB Jul 31, 2026
4c8c554
update message - data order in consumer card
Brett-S-OWB Jul 31, 2026
0386e27
linting
benderl Aug 7, 2026
b589275
reapply lost changes from #3715
benderl Aug 7, 2026
a4d735b
usage type self controlled (#3775)
LKuemmel Aug 7, 2026
f93cb60
review (#3776)
LKuemmel Aug 7, 2026
269330e
calc on time (#3777)
LKuemmel Aug 7, 2026
579d440
pytest (#3786)
LKuemmel Aug 10, 2026
ef3c7d1
fixes (#3794)
LKuemmel Aug 11, 2026
7d96a3b
Reworked IDM and Lambda consumer and add Ovum (#3737)
seaspotter Aug 17, 2026
c526e3b
split PR: remove moduls except mqtt
LKuemmel Aug 18, 2026
5ee18ea
fix loadmanagement, clean up (#3819)
LKuemmel Aug 18, 2026
506e97e
fix versions
LKuemmel Aug 19, 2026
5cea084
fix merge error
LKuemmel Aug 21, 2026
55966c3
consider non charging for consumers and update consumer state message…
LKuemmel Aug 21, 2026
da5e03f
fix rebase
LKuemmel Aug 24, 2026
3032365
pass data object as parameter in set_power_limit methode (#3854)
LKuemmel Aug 25, 2026
b360705
fix some type hints (#3866)
benderl Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion data/config/mosquitto/local/openwb_local.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# openwb-version:20
# openwb-version:21
listener 1886 localhost
allow_anonymous true

Expand Down Expand Up @@ -28,6 +28,15 @@ topic openWB/chargepoint/+/get/# out 2
topic openWB/chargepoint/+/config/# out 2
topic openWB/chargepoint/template/# out 2

topic openWB/consumer/get/# out 2

topic openWB/consumer/+/module out 2
topic openWB/consumer/+/config out 2
topic openWB/consumer/+/extra_meter out 2
topic openWB/consumer/+/usage out 2
topic openWB/consumer/+/get/# out 2
topic openWB/consumer/+/set/# out 2

topic openWB/internal_chargepoint/# out 2

topic openWB/io/# out 2
Expand Down
106 changes: 105 additions & 1 deletion data/config/mosquitto/public/default-dynamic-security.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"anonymousGroup": "anonymous",
"roles": [
{
"rolename": "openwb-version:8",
"rolename": "openwb-version:9",
"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 @@ -1097,6 +1097,24 @@
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/general/chargemode_config/surplus/consumer/switch_on_delay",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/general/chargemode_config/surplus/consumer/switch_off_delay",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/general/chargemode_config/surplus/consumer/switch_off_threshold",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/security/access/SurplusChargeConfiguration",
Expand Down Expand Up @@ -1156,6 +1174,24 @@
"topic": "openWB/general/chargemode_config/surplus/vehicle/switch_off_delay",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/chargemode_config/surplus/consumer/switch_on_delay",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/chargemode_config/surplus/consumer/switch_off_delay",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/chargemode_config/surplus/consumer/switch_off_threshold",
"priority": 0,
"allow": true
}
]
},
Expand Down Expand Up @@ -1733,6 +1769,61 @@
}
]
},
{
"rolename": "consumer-configuration-access",
"textname": "Zugang zu den Verbrauchereinstellungen",
"textdescription": "Erlaubt den Zugang zu den Verbrauchereinstellungen.",
"acls": [
{
"acltype": "publishClientSend",
"topic": "openWB/consumer/+/config",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/consumer/+/module",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/consumer/+/usage",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/+/config",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/+/module",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/+/usage",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/system/configurable/consumers",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/optional/ep/configured",
"priority": 0,
"allow": true
}
]
},
{
"rolename": "vehicle-configuration-access",
"textname": "Zugang zu den Fahrzeugeinstellungen",
Expand Down Expand Up @@ -2837,6 +2928,19 @@
}
]
},
{
"rolename": "consumer-sum-access",
"textname": "Leserechte für Verbrauchersummendaten",
"textdescription": "Erlaubt das Empfangen von Daten für die Summe aller Verbraucher.",
"acls": [
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/get/+",
"priority": 0,
"allow": true
}
]
},
{
"rolename": "tenant-energy-configuration-access",
"textname": "Leserechte für Mieterstromdaten",
Expand Down
56 changes: 56 additions & 0 deletions data/config/mosquitto/public/role-templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,62 @@
"allow": true
}
]
},
{
"rolename": "consumer-<id>-access",
"textname": "Leserechte für Verbraucher <id>",
"textdescription": "Erlaubt das Empfangen von Daten für Verbraucher <id>.",
"acls": [
{
"acltype": "publishClientSend",
"topic": "openWB/set/consumer/<id>/usage",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/command/+/todo/addConsumerSchedulePlan",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientSend",
"topic": "openWB/set/command/+/todo/addConsumerTimePlan",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/consumer/<id>/#",
"priority": 0,
"allow": true
}
]
},
{
"rolename": "consumer-<id>-write-access",
"textname": "Schreibrechte für MQTT-Verbraucher <id>",
"textdescription": "Erlaubt das Senden von Daten für Verbraucher <id>.",
"acls": [
{
"acltype": "publishClientSend",
"topic": "openWB/set/mqtt/consumer/<id>/get/#",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/set/mqtt/consumer/<id>/get/#",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/mqtt/consumer/<id>/#",
"priority": 0,
"allow": true
}
]
},
{
"rolename": "vehicle-<id>-access",
Expand Down
65 changes: 33 additions & 32 deletions packages/control/algorithm/additional_current.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import logging

from control import data
from control.algorithm import common
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT
from control.limiting_value import LoadmanagementLimit
from control.load_protocol import Load
from control.loadmanagement import Loadmanagement
from control.chargepoint.chargepoint import Chargepoint
from control.algorithm.filter_chargepoints import (get_chargepoints_by_mode_and_counter,
get_preferenced_chargepoint_charging)
from control.algorithm.filter_chargepoints import (filtered_loads_to_str, get_grouped_loads_by_mode_and_counter,
get_preferenced_load_charging)

log = logging.getLogger(__name__)

Expand All @@ -19,44 +18,46 @@ def __init__(self) -> None:

def set_additional_current(self) -> None:
common.reset_current_by_chargemode(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT)
for mode_tuple, counter in common.mode_and_counter_generator(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT):
preferenced_chargepoints, preferenced_cps_without_set_current = get_preferenced_chargepoint_charging(
get_chargepoints_by_mode_and_counter(mode_tuple, f"counter{counter.num}"))
if preferenced_chargepoints:
common.update_raw_data(preferenced_chargepoints)
log.info(f"Mode-Tuple {mode_tuple[0]} - {mode_tuple[1]} - {mode_tuple[2]}, Zähler {counter.num}")
for cp, group in data.data.counter_all_data.generator_cps_by_loadmanagement_prios(
preferenced_chargepoints):
missing_currents, counts = common.get_missing_currents_left(group)
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter, cp)
log.debug(f"cp {cp.num} available currents {available_currents} missing currents "
for counter in common.counter_generator():
preferenced_loads_groups, preferenced_loads_without_set_current = get_preferenced_load_charging(
get_grouped_loads_by_mode_and_counter(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT,
f"counter{counter.num}"))
for preferenced_loads in preferenced_loads_groups:
common.update_raw_data(preferenced_loads)
if len(preferenced_loads) > 0:
log.info(f"Zähler {counter.num}, Verbraucher {filtered_loads_to_str(preferenced_loads)}")
while len(preferenced_loads):
load = preferenced_loads[0]
missing_currents, counts = common.get_missing_currents_left(preferenced_loads)
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter, load)
log.debug(f"load {load.num} available currents {available_currents} missing currents "
f"{missing_currents} limit {limit.message}")
if limit.limiting_value is not None:
cp.data.control_parameter.limit = limit
available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents)
load.data.control_parameter.limit = limit
available_for_cp = common.available_current_for_load(
load, counts, available_currents, missing_currents)
current = common.get_current_to_set(
cp.data.set.current, available_for_cp, cp.data.set.target_current)
self._set_loadmangement_message(current, limit, cp)
load.data.set.current, available_for_cp, load.data.set.target_current)
self._set_loadmangement_message(current, limit, load)
common.set_current_counterdiff(
cp.data.control_parameter.min_current,
load.data.control_parameter.min_current,
current,
cp)
if preferenced_cps_without_set_current:
for cp in preferenced_cps_without_set_current:
cp.data.set.current = cp.data.set.target_current
load)
preferenced_loads.pop(0)
for load in preferenced_loads_without_set_current:
load.data.set.current = load.data.set.target_current

# tested
def _set_loadmangement_message(self,
current: float,
limit: LoadmanagementLimit,
chargepoint: Chargepoint) -> None:
load: Load) -> 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}")
if (current != max(chargepoint.data.set.target_current, chargepoint.data.set.current or 0) and
f"current {current} target {load.data.set.target_current} set current {load.data.set.current}"
f" required currents {load.data.control_parameter.required_currents}")
if (current != max(load.data.set.target_current, load.data.set.current or 0) and
# Strom erreicht nicht die vorgegebene Stromstärke
round(current, 2) != round(max(
chargepoint.data.control_parameter.required_currents), 2)):
chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden"
f"{limit.message}")
load.data.control_parameter.required_currents), 2)):
load.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden"
f"{limit.message}")
55 changes: 28 additions & 27 deletions packages/control/algorithm/bidi_charging.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
from control import data
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE
from control.algorithm.filter_chargepoints import get_chargepoints_with_required_current_by_chargemode
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

log = logging.getLogger(__name__)
Expand All @@ -15,30 +16,30 @@ def set_bidi(self):
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
for mode_tuple in CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE:
preferenced_cps = get_chargepoints_with_required_current_by_chargemode(mode_tuple)
if preferenced_cps:
log.info(
f"Mode-Tuple {mode_tuple[0]} - {mode_tuple[1]} - {mode_tuple[2]}, Zähler {grid_counter.num}")
for cp, group in data.data.counter_all_data.generator_cps_by_loadmanagement_prios(preferenced_cps):
zero_point_adjustment = grid_counter.data.set.surplus_power_left / len(group)
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] = 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:
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)]
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")
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)
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)
Loading