Skip to content

Commit e8e11c0

Browse files
authored
Merge branch 'openWB:master' into sonnenbatterie-multipe-consumption-counters
2 parents 7ff6e94 + 14175bc commit e8e11c0

333 files changed

Lines changed: 887 additions & 605 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/control/bat_all.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,13 @@ def _get_charging_power_left(self):
351351
# ev wird nach Speicher geladen
352352
elif config.bat_mode == BatConsiderationMode.EV_MODE.value:
353353
# Speicher sollte weder ge- noch entladen werden.
354-
charging_power_left = self.data.get.power
354+
# wenn aktive Speichersteuerung in Höhe PV-Leistung lädt
355+
# hat Speicher Priorität vor EV-Ladung
356+
if (self.data.config.bat_control_activated and
357+
self.data.config.power_limit_mode == BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value):
358+
charging_power_left = 0
359+
else:
360+
charging_power_left = self.data.get.power
355361
else:
356362
# Speicher soll geladen werden um min SoC zu erreichen
357363
if self.data.get.soc < config.min_bat_soc:
@@ -385,14 +391,29 @@ def _get_charging_power_left(self):
385391
# Speicher darf wegen Hysterese bis min_bat_soc entladen werden.
386392
else:
387393
if self.data.set.power_limit is None:
388-
if config.bat_power_discharge_active:
394+
# set allowed power
395+
if (self.data.config.bat_control_activated and
396+
self.data.config.power_limit_mode ==
397+
BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value):
398+
base_power = 0
399+
else:
400+
base_power = self.data.get.power
401+
402+
# Aktive Steuerung nicht konfiguriert oder
403+
# Aktive Steuerung + Preisgrenze aktiv + Grenze nicht unterschritten
404+
# -> dann erlaubte Speicherentladeleistung addieren
405+
power_discharge_allowed = (self.data.config.bat_control_activated is False or
406+
(self.data.config.power_limit_condition ==
407+
BatPowerLimitCondition.PRICE_LIMIT.value and
408+
self.data.set.power_limit is None))
409+
if config.bat_power_discharge_active and power_discharge_allowed:
389410
# Wenn der Speicher mit mehr als der erlaubten Entladeleistung entladen wird, muss das
390411
# vom Überschuss subtrahiert werden.
391-
charging_power_left = config.bat_power_discharge + self.data.get.power
412+
charging_power_left = config.bat_power_discharge + base_power
392413
log.debug(f"Erlaubte Entlade-Leistung nutzen {charging_power_left}W")
393414
else:
394415
# Speicher sollte weder ge- noch entladen werden.
395-
charging_power_left = self.data.get.power
416+
charging_power_left = base_power
396417
else:
397418
log.debug("Keine erlaubte Entladeleistung freigeben, da der Speicher mit einer vorgegeben "
398419
"Leistung entladen wird.")
@@ -401,14 +422,28 @@ def _get_charging_power_left(self):
401422
else:
402423
self.data.set.hysteresis_discharge = True
403424
if self.data.set.power_limit is None:
404-
if config.bat_power_discharge_active:
425+
# set allowed power
426+
if (self.data.config.bat_control_activated and
427+
self.data.config.power_limit_mode == BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value):
428+
base_power = 0
429+
else:
430+
base_power = self.data.get.power
431+
432+
# Aktive Steuerung nicht konfiguriert oder
433+
# Aktive Steuerung + Preisgrenze aktiv + Grenze nicht unterschritten
434+
# -> dann erlaubte Speicherentladeleistung addieren
435+
power_discharge_allowed = (self.data.config.bat_control_activated is False or
436+
(self.data.config.power_limit_condition ==
437+
BatPowerLimitCondition.PRICE_LIMIT.value and
438+
self.data.set.power_limit is None))
439+
if config.bat_power_discharge_active and power_discharge_allowed:
405440
# Wenn der Speicher mit mehr als der erlaubten Entladeleistung entladen wird, muss das
406441
# vom Überschuss subtrahiert werden.
407-
charging_power_left = config.bat_power_discharge + self.data.get.power
442+
charging_power_left = config.bat_power_discharge + base_power
408443
log.debug(f"Erlaubte Entlade-Leistung nutzen {charging_power_left}W")
409444
else:
410445
# Speicher sollte weder ge- noch entladen werden.
411-
charging_power_left = self.data.get.power
446+
charging_power_left = base_power
412447
else:
413448
log.debug("Keine erlaubte Entladeleistung freigeben, da der Speicher mit einer vorgegeben "
414449
"Leistung entladen wird.")

packages/control/optional.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def flexible_tariff_module(self) -> TypingOptional[ConfigurableFlexibleTariff]:
4242
def flexible_tariff_module(self, value: TypingOptional[ConfigurableFlexibleTariff]):
4343
if (value is None or
4444
(self._flexible_tariff_module and value and
45-
self._flexible_tariff_module.config.name != value.config.name)):
45+
self._flexible_tariff_module.config != value.config)):
4646
self.data.electricity_pricing.flexible_tariff.get = PricingGet()
4747
self._reset_state(self.data.electricity_pricing.flexible_tariff)
4848
self._flexible_tariff_module = value
@@ -56,7 +56,7 @@ def grid_fee_module(self) -> TypingOptional[ConfigurableGridFee]:
5656
def grid_fee_module(self, value: TypingOptional[ConfigurableGridFee]):
5757
if (value is None or
5858
(self._grid_fee_module and value and
59-
self._grid_fee_module.config.name != value.config.name)):
59+
self._grid_fee_module.config != value.config)):
6060
self.data.electricity_pricing.grid_fee.get = PricingGet()
6161
self._reset_state(self.data.electricity_pricing.grid_fee)
6262
self._grid_fee_module = value

packages/helpermodules/update_config.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757

5858
class UpdateConfig:
5959

60-
DATASTORE_VERSION = 124
60+
DATASTORE_VERSION = 126
6161

6262
valid_topic = [
6363
"^openWB/bat/config/bat_control_activated$",
@@ -3132,3 +3132,48 @@ def upgrade(topic: str, payload) -> Optional[dict]:
31323132
return {topic: configuration_payload}
31333133
self._loop_all_received_topics(upgrade)
31343134
self._append_datastore_version(124)
3135+
3136+
def upgrade_datastore_125(self) -> None:
3137+
def upgrade(topic: str, payload) -> None:
3138+
if "openWB/optional/ep/flexible_tariff/provider" == topic:
3139+
provider = decode_payload(payload)
3140+
if provider["type"] == "energycharts":
3141+
surcharge = provider["configuration"]["surcharge"]
3142+
if surcharge > 0.1: # entspricht mehr als 0.1ct/kWh konfiguriertem Zuschlag
3143+
provider["configuration"]["surcharge"] = surcharge / 100000
3144+
return {topic: provider}
3145+
if ("openWB/optional/ep/flexible_tariff/provider" == topic or
3146+
"openWB/optional/ep/grid_fee/provider" == topic):
3147+
provider = decode_payload(payload)
3148+
if provider["type"] == "fixed_hours":
3149+
changed = False
3150+
for i, tarif in enumerate(provider["configuration"]["tariffs"]):
3151+
price = tarif.get("price", 0)
3152+
if price > 0.001: # entspricht mehr als 0,1ct/kWh konfiguriertem Tarifpreis
3153+
provider["configuration"]["tariffs"][i]["price"] = price / 1000
3154+
changed = True
3155+
default_price = provider["configuration"].get("default_price", 0)
3156+
if default_price > 0.001:
3157+
provider["configuration"]["default_price"] = default_price / 1000
3158+
changed = True
3159+
if changed:
3160+
return {topic: provider}
3161+
self._loop_all_received_topics(upgrade)
3162+
self._append_datastore_version(125)
3163+
3164+
def upgrade_datastore_126(self) -> None:
3165+
def upgrade(topic: str, payload) -> None:
3166+
if "openWB/optional/ep/flexible_tariff/provider" == topic:
3167+
provider = decode_payload(payload)
3168+
if provider.get("type") == "energycharts":
3169+
changed = False
3170+
if provider["configuration"].get("net") is None:
3171+
provider["configuration"]["net"] = True
3172+
changed = True
3173+
if provider["configuration"].get("tax") is None:
3174+
provider["configuration"]["tax"] = 19
3175+
changed = True
3176+
if changed:
3177+
return {topic: provider}
3178+
self._loop_all_received_topics(upgrade)
3179+
self._append_datastore_version(126)

packages/helpermodules/update_config_test.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,87 @@ def test_upgrade_datastore_122(name, monkeypatch):
149149
# Assert
150150
assert mock_dump.call_args_list[0].args[0] == expected_content
151151
assert uc.all_received_topics["openWB/system/datastore_version"] == [122]
152+
153+
154+
def test_upgrade_datastore_125_converts_only_tariff_prices_and_persists(mock_pub):
155+
uc = UpdateConfig()
156+
uc.all_received_topics = {
157+
"openWB/optional/ep/flexible_tariff/provider": {
158+
"type": "fixed_hours",
159+
"configuration": {
160+
"tariffs": [
161+
{"price": 0.15},
162+
{"price": 0.09},
163+
],
164+
"default_price": 0.01,
165+
},
166+
},
167+
"openWB/system/datastore_version": [123],
168+
}
169+
170+
uc.upgrade_datastore_125()
171+
172+
provider = uc.all_received_topics["openWB/optional/ep/flexible_tariff/provider"]
173+
assert provider["configuration"]["tariffs"][0]["price"] == pytest.approx(0.00015)
174+
assert provider["configuration"]["tariffs"][1]["price"] == pytest.approx(0.00009)
175+
assert provider["configuration"]["default_price"] == pytest.approx(0.00001)
176+
assert uc.all_received_topics["openWB/system/datastore_version"] == [123, 125]
177+
178+
updated_topics = [call.args[0] for call in mock_pub.pub.call_args_list]
179+
assert "openWB/optional/ep/flexible_tariff/provider" in updated_topics
180+
assert "openWB/system/datastore_version" in updated_topics
181+
182+
183+
def test_upgrade_datastore_125_converts_energycharts_surcharge(mock_pub):
184+
uc = UpdateConfig()
185+
uc.all_received_topics = {
186+
"openWB/optional/ep/flexible_tariff/provider": {
187+
"type": "energycharts",
188+
"configuration": {
189+
"surcharge": 10,
190+
},
191+
},
192+
"openWB/system/datastore_version": [123],
193+
}
194+
195+
uc.upgrade_datastore_125()
196+
197+
provider = uc.all_received_topics["openWB/optional/ep/flexible_tariff/provider"]
198+
assert provider["configuration"]["surcharge"] == pytest.approx(0.0001)
199+
assert uc.all_received_topics["openWB/system/datastore_version"] == [123, 125]
200+
201+
updated_topics = [call.args[0] for call in mock_pub.pub.call_args_list]
202+
assert "openWB/optional/ep/flexible_tariff/provider" in updated_topics
203+
204+
205+
def test_upgrade_datastore_125_is_idempotent_for_already_converted_values(mock_pub):
206+
uc = UpdateConfig()
207+
uc.all_received_topics = {
208+
"openWB/optional/ep/flexible_tariff/provider": {
209+
"type": "energycharts",
210+
"configuration": {
211+
"surcharge": 0.00015,
212+
},
213+
},
214+
"openWB/optional/ep/grid_fee/provider": {
215+
"type": "fixed_hours",
216+
"configuration": {
217+
"tariffs": [
218+
{"price": 0.00006},
219+
{"price": 0.00099},
220+
],
221+
"default_price": 0.00008,
222+
},
223+
},
224+
"openWB/system/datastore_version": [123, 124],
225+
}
226+
227+
expected_flexible = json.loads(json.dumps(uc.all_received_topics["openWB/optional/ep/flexible_tariff/provider"]))
228+
expected_grid_fee = json.loads(json.dumps(uc.all_received_topics["openWB/optional/ep/grid_fee/provider"]))
229+
230+
uc.upgrade_datastore_125()
231+
232+
assert uc.all_received_topics["openWB/optional/ep/flexible_tariff/provider"] == expected_flexible
233+
assert uc.all_received_topics["openWB/optional/ep/grid_fee/provider"] == expected_grid_fee
234+
assert uc.all_received_topics["openWB/system/datastore_version"] == [123, 124, 125]
235+
assert mock_pub.pub.call_count == 1 # einmal publishen für Upgrade der Datastore-Version

packages/modules/common/store/_tariff.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ def __reduce_prices(prices: Dict[str, float]) -> Dict[int, float]:
7272

7373
def __get_default_grid_fee_price(prices: Dict[str, float]) -> float:
7474
if (data.data.optional_data.grid_fee_module is not None and
75-
hasattr(data.data.optional_data.grid_fee_module.config, "default_price") and
76-
data.data.optional_data.grid_fee_module.config.default_price is not None):
75+
hasattr(data.data.optional_data.grid_fee_module.config.configuration, "default_price") and
76+
data.data.optional_data.grid_fee_module.config.configuration.default_price is not None):
7777
log.debug("Using default grid fee price from configuration: " +
78-
f"{data.data.optional_data.grid_fee_module.config.default_price}")
79-
return data.data.optional_data.grid_fee_module.config.default_price
78+
f"{data.data.optional_data.grid_fee_module.config.configuration.default_price}")
79+
return data.data.optional_data.grid_fee_module.config.configuration.default_price
8080
else:
8181
# Fallback: Median der vorhandenen Netzentgelte wird
8282
# als Schätzung für das Standard-Netzentgelt verwenden
@@ -95,8 +95,8 @@ def __normalize_grid_fee_prices(grid_fee_prices: Dict[int, float], max_timestamp
9595
grid_fee_prices = {int(float(k)): v for k, v in grid_fee_prices.items() if int(float(k)) <= max_timestamp}
9696

9797
if (data.data.optional_data.flexible_tariff_module is not None and
98-
hasattr(data.data.optional_data.flexible_tariff_module.config, "includes_grid_fee") and
99-
data.data.optional_data.flexible_tariff_module.config.includes_grid_fee is False):
98+
hasattr(data.data.optional_data.flexible_tariff_module.config.configuration, "includes_grid_fee") and
99+
data.data.optional_data.flexible_tariff_module.config.configuration.includes_grid_fee is False):
100100
return grid_fee_prices
101101
else:
102102
# Bei flexiblen Tarifen, die das Netzentgelt _nicht_ enthalten,

packages/modules/common/store/_tariff_test.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from datetime import timedelta
22
import datetime
3+
from types import SimpleNamespace
34
from typing import Dict
45
from unittest.mock import Mock
56

@@ -30,12 +31,16 @@ def mock_data() -> None:
3031
data.data_init(Mock())
3132
data.data.optional_data = Optional()
3233
data.data.optional_data.grid_fee_module = Mock()
33-
data.data.optional_data.grid_fee_module.config = Mock()
34-
data.data.optional_data.grid_fee_module.config.default_price = None
34+
data.data.optional_data.grid_fee_module.config = SimpleNamespace(
35+
configuration=SimpleNamespace(default_price=None)
36+
)
3537
data.data.optional_data.flexible_tariff_module = Mock()
36-
data.data.optional_data.flexible_tariff_module.config = Mock()
37-
data.data.optional_data.flexible_tariff_module.config.default_price = None
38-
data.data.optional_data.flexible_tariff_module.config.includes_grid_fee = True
38+
data.data.optional_data.flexible_tariff_module.config = SimpleNamespace(
39+
configuration=SimpleNamespace(
40+
default_price=None,
41+
includes_grid_fee=True
42+
)
43+
)
3944

4045

4146
@pytest.mark.parametrize(
@@ -146,8 +151,8 @@ def test_sum_prices(
146151
flexible_tariff
147152
)
148153
data.data.optional_data.data.electricity_pricing.grid_fee.get.prices = grid_fee
149-
data.data.optional_data.flexible_tariff_module.config.includes_grid_fee = includes_grid_fee
150-
data.data.optional_data.grid_fee_module.config.default_price = default_grid_fee_price
154+
data.data.optional_data.flexible_tariff_module.config.configuration.includes_grid_fee = includes_grid_fee
155+
data.data.optional_data.grid_fee_module.config.configuration.default_price = default_grid_fee_price
151156
summed = value_Store.sum_prices()
152157
assert summed == expected_prices
153158

0 commit comments

Comments
 (0)