From e5122b595af885ab6c998d53404616c171626c87 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Fri, 17 Apr 2026 14:08:07 +0200 Subject: [PATCH 1/4] time charging: min bat soc --- packages/control/bat_all.py | 20 +++++ packages/control/bat_all_test.py | 88 +++++++++++++++++++++ packages/control/ev/charge_template.py | 30 ++++--- packages/control/ev/charge_template_test.py | 39 +++++++++ packages/helpermodules/abstract_plans.py | 1 + packages/helpermodules/update_config.py | 13 ++- 6 files changed, 179 insertions(+), 12 deletions(-) diff --git a/packages/control/bat_all.py b/packages/control/bat_all.py index 4cc61cf35d..a4010292cd 100644 --- a/packages/control/bat_all.py +++ b/packages/control/bat_all.py @@ -608,6 +608,26 @@ def get_power_limit(self): else: self.data.set.current_state = CurrentState.ACTIVE.value + def time_charging_min_bat_soc_allowed(self) -> bool: + if self.data.config.configured and self.data.config.bat_control_permitted and self.data.config.bat_control_activated: + if (self.data.config.power_limit_condition == BatPowerLimitCondition.MANUAL.value or + self.data.config.power_limit_condition == BatPowerLimitCondition.VEHICLE_CHARGING.value): + return False + elif self.data.config.power_limit_condition == BatPowerLimitCondition.PRICE_LIMIT.value: + if self.data.config.power_limit_mode == BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value: + if ((self.data.config.price_limit_activated and + data.data.optional_data.ep_is_charging_allowed_price_threshold( + self.data.config.price_limit)) + or (self.data.config.price_charge_activated and + data.data.optional_data.ep_is_charging_allowed_price_threshold( + self.data.config.charge_limit))): + return True + else: + return False + else: + return + return True + def get_controllable_bat_components() -> List: bat_components = [] diff --git a/packages/control/bat_all_test.py b/packages/control/bat_all_test.py index ee9cf5fe4e..414f7eac44 100644 --- a/packages/control/bat_all_test.py +++ b/packages/control/bat_all_test.py @@ -367,3 +367,91 @@ def test_control_price_limit(params: BatControlParams, data_, monkeypatch): data.data.bat_all_data._set_bat_power_active_control(data.data.bat_all_data.data.set.power_limit) assert data.data.bat_data["bat2"].data.set.power_limit == params.expected_power_limit_bat + + +@pytest.mark.parametrize( + "control_permitted, control_activated, condition, limit, expected_result", + [ + pytest.param(False, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, True, + id="Speichersteuerung nicht erlaubt, aber aktiviert -> laden"), + pytest.param(True, False, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, True, + id="Speichersteuerung erlaubt, aber nicht aktiviert -> laden"), + pytest.param(True, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, + id="Manuell, volle Entladesperre -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, + id="Manuell, Entladung in Fahrzeuge sperren -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value, False, + id="Manuell, PV-Ertrag speichern -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, + id="Fahrzeuge laden, volle Entladesperre -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, + id="Fahrzeuge laden, Entladung in Fahrzeuge sperren -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value, False, + id="Fahrzeuge laden, PV-Ertrag speichern -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.PRICE_LIMIT.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, + id="Preislimit, volle Entladesperre -> nicht laden"), + pytest.param(True, True, BatPowerLimitCondition.PRICE_LIMIT.value, BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, + id="Preislimit, Entladung in Fahrzeuge sperren -> nicht laden"), + + ] +) +def test_time_charging_min_bat_soc_allowed(control_permitted: bool, + control_activated: bool, + condition: BatPowerLimitCondition, + limit: BatPowerLimitMode, + expected_result: bool): + # setup + b = BatAll() + b.data.config.power_limit_condition = condition + b.data.config.power_limit_mode = limit + b.data.config.bat_control_permitted = control_permitted + b.data.config.bat_control_activated = control_activated + + # execution + result = b.time_charging_min_bat_soc_allowed() + + # evaluation + assert result == expected_result + + +@pytest.mark.parametrize( + "ep_configured, price_limit_activated, price_charge_activated, price_threshold_mock, expected_result", + [ + pytest.param(False, True, True, [True, True], True, + id="Preislimit aktiviert, aber kein Preis konfiguriert -> Eigenregelung -> laden"), + pytest.param(True, True, False, [True], True, + id="Strompreis für Regelmodus, Preis unter Limit -> laden"), + pytest.param(True, True, False, [False], False, + id="Strompreis für Regelmodus, Preis über Limit -> nicht laden"), + pytest.param(True, False, True, [True], True, + id="Strompreis für aktives Laden, Preis unter Limit -> laden"), + pytest.param(True, False, True, [False], False, + id="Strompreis für aktives Laden, Preis unter Limit -> nicht laden"), + pytest.param(True, False, False, [], False, + id="beide Strompreise deaktiviert -> nicht laden"), + ] +) +def test_time_charging_min_bat_soc_allowed(ep_configured: bool, + price_limit_activated: bool, + price_charge_activated: bool, + price_threshold_mock: List[bool], + expected_result: bool, + monkeypatch): + # setup + b = BatAll() + b.data.config.power_limit_condition = BatPowerLimitCondition.PRICE_LIMIT.value + b.data.config.power_limit_mode = BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value + b.data.config.price_limit_activated = price_limit_activated + b.data.config.price_charge_activated = price_charge_activated + data.data.optional_data.data.electricity_pricing.configured = ep_configured + b.data.config.bat_control_permitted = True + b.data.config.bat_control_activated = True + + monkeypatch.setattr(data.data.optional_data, "ep_is_charging_allowed_price_threshold", + Mock(side_effect=price_threshold_mock)) + + # execution + result = b.time_charging_min_bat_soc_allowed() + + # evaluation + assert result == expected_result diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index 1a235c1422..e66ec60e4f 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -143,6 +143,10 @@ class ChargeTemplate: TIME_CHARGING_NO_PLAN_ACTIVE = "Kein Zeitfenster für Zeitladen aktiv." TIME_CHARGING_SOC_REACHED = "Das Ladeziel für das Zeitladen wurde erreicht." TIME_CHARGING_AMOUNT_REACHED = "Die gewünschte Energiemenge für das Zeitladen wurde geladen." + TIME_CHARGING_CONFLICT_ACTIVE_BAT_CONTROL = ("Laden mit Zeitladen nach Speicher-SoC nicht möglich, da dies im " + "Konflikt mit der aktiven Speichersteuerung steht.") + TIME_CHARGING_MIN_BAT_SOC_REACHED = ("Laden mit Zeitladen nach Speicher-SoC nicht möglich, da der minimale SoC des" + " Speichers erreicht wurde.") def time_charging(self, soc: Optional[float], @@ -151,34 +155,38 @@ def time_charging(self, """ prüft, ob ein Zeitfenster aktiv ist und setzt entsprechend den Ladestrom """ message = None - sub_mode = "time_charging" + sub_mode = "stop" + current = 0 id = None phases = None try: if self.data.time_charging.plans: plan = timecheck.check_plans_timeframe(self.data.time_charging.plans) if plan is not None: - current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current - phases = plan.phases_to_use id = plan.id + phases = plan.phases_to_use if plan.limit.selected == "soc" and soc and soc >= plan.limit.soc: # SoC-Limit erreicht - current = 0 - sub_mode = "stop" message = self.TIME_CHARGING_SOC_REACHED elif plan.limit.selected == "amount" and used_amount_time_charging >= plan.limit.amount: # Energie-Limit erreicht - current = 0 - sub_mode = "stop" message = self.TIME_CHARGING_AMOUNT_REACHED + elif plan.min_bat_soc is not None: + if data.data.bat_all_data.time_charging_min_bat_soc_allowed(): + if data.data.bat_all_data.data.get.soc < plan.min_bat_soc: + message = self.TIME_CHARGING_MIN_BAT_SOC_REACHED + else: + current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current + sub_mode = "time_charging" + else: + message = self.TIME_CHARGING_CONFLICT_ACTIVE_BAT_CONTROL + else: + current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current + sub_mode = "time_charging" else: message = self.TIME_CHARGING_NO_PLAN_ACTIVE - current = 0 - sub_mode = "stop" else: message = self.TIME_CHARGING_NO_PLAN_CONFIGURED - current = 0 - sub_mode = "stop" return current, sub_mode, message, id, phases except Exception: log.exception("Fehler im ev-Modul "+str(self.data.id)) diff --git a/packages/control/ev/charge_template_test.py b/packages/control/ev/charge_template_test.py index 68fe7c4d83..162bd2ba67 100644 --- a/packages/control/ev/charge_template_test.py +++ b/packages/control/ev/charge_template_test.py @@ -6,6 +6,7 @@ from control import data from control import optional +from control.bat_all import BatAll from control.chargepoint.control_parameter import ControlParameter from control.ev.charge_template import SelectedPlan from control.chargepoint.charging_type import ChargingType @@ -73,6 +74,44 @@ def test_time_charging(plans: Dict[int, TimeChargingPlan], soc: float, used_amou assert ret == expected +@pytest.mark.parametrize( + "min_bat_soc, charging_allowed_mock, soc, expected", + [ + pytest.param(80, False, 81, (0, "stop", ChargeTemplate.TIME_CHARGING_CONFLICT_ACTIVE_BAT_CONTROL, 0, 1), + id="Konflikt mit aktiver Speichersteuerung -> nicht laden"), + pytest.param(80, True, 79, (0, "stop", ChargeTemplate.TIME_CHARGING_MIN_BAT_SOC_REACHED, 0, 1), + id="Mindest-SoC des Speichers unterschritten -> nicht laden"), + pytest.param(80, True, 80, (16, "time_charging", None, 0, 1), id="laden erlaubt"), + pytest.param(None, True, 80, (16, "time_charging", None, 0, 1), + id="Mindest-SoC-Beachtung nicht konfiguriert, laden erlaubt"), + ] +) +def test_time_charging_min_bat_soc(min_bat_soc: Optional[int], + charging_allowed_mock: bool, + soc: float, + expected: Tuple[int, str, Optional[str], Optional[str], int], + monkeypatch): + # setup + ct = ChargeTemplate() + plan = TimeChargingPlan(id=0) + plan.min_bat_soc = min_bat_soc + ct.data.time_charging.plans = [plan] + check_plans_timeframe_mock = Mock(return_value=plan) + monkeypatch.setattr(timecheck, "check_plans_timeframe", check_plans_timeframe_mock) + + data.data.bat_all_data = BatAll() + data.data.bat_all_data.data.config.configured = True + data.data.bat_all_data.data.get.soc = soc + monkeypatch.setattr(data.data.bat_all_data, "time_charging_min_bat_soc_allowed", + Mock(return_value=charging_allowed_mock)) + + # execution + ret = ct.time_charging(soc, 100, ChargingType.AC.value) + + # evaluation + assert ret == expected + + @pytest.mark.parametrize( "selected, current_soc, used_amount, expected", [ diff --git a/packages/helpermodules/abstract_plans.py b/packages/helpermodules/abstract_plans.py index 6c7e3ff78f..20b3ce6b98 100644 --- a/packages/helpermodules/abstract_plans.py +++ b/packages/helpermodules/abstract_plans.py @@ -97,6 +97,7 @@ class TimeChargingPlan(TimeframePlan): dc_current: float = 145 id: Optional[int] = None limit: Limit = field(default_factory=limit_factory) + min_bat_soc: Optional[int] = None name: str = "neuer Zeitladen-Plan" phases_to_use: int = 1 diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 3384fdd9ef..587d4eff06 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -57,7 +57,7 @@ class UpdateConfig: - DATASTORE_VERSION = 121 + DATASTORE_VERSION = 122 valid_topic = [ "^openWB/bat/config/bat_control_permitted$", @@ -3068,3 +3068,14 @@ def upgrade(topic: str, payload) -> Optional[dict]: return {topic: payload} self._loop_all_received_topics(upgrade) self._append_datastore_version(121) + + def upgrade_datastore_122(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + 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.get("min_bat_soc") is None: + plan.update({"min_bat_soc": None}) + return {topic: payload} + self._loop_all_received_topics(upgrade) + # self._append_datastore_version(122) From 1c3e13ed37724f0938d854f1985e8927c2c0ef37 Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Fri, 17 Apr 2026 14:29:11 +0200 Subject: [PATCH 2/4] log --- packages/control/ev/charge_template.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index e66ec60e4f..d4b5c05fcf 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -176,6 +176,7 @@ def time_charging(self, if data.data.bat_all_data.data.get.soc < plan.min_bat_soc: message = self.TIME_CHARGING_MIN_BAT_SOC_REACHED else: + log.debug("Zeitladen: minimaler Speicher-SoC überschritten, Laden mit Zeitladen möglich.") current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current sub_mode = "time_charging" else: From 0d7edc9d2435d6f1f72222d27b96dea2e0c959cf Mon Sep 17 00:00:00 2001 From: LKuemmel Date: Fri, 17 Apr 2026 14:41:38 +0200 Subject: [PATCH 3/4] review --- packages/control/bat_all.py | 6 ++-- packages/control/bat_all_test.py | 48 ++++++++++++++++--------- packages/control/ev/charge_template.py | 5 +-- packages/helpermodules/update_config.py | 2 +- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/packages/control/bat_all.py b/packages/control/bat_all.py index a4010292cd..ea4f7704ed 100644 --- a/packages/control/bat_all.py +++ b/packages/control/bat_all.py @@ -609,7 +609,9 @@ def get_power_limit(self): self.data.set.current_state = CurrentState.ACTIVE.value def time_charging_min_bat_soc_allowed(self) -> bool: - if self.data.config.configured and self.data.config.bat_control_permitted and self.data.config.bat_control_activated: + if (self.data.config.configured and + self.data.config.bat_control_permitted and + self.data.config.bat_control_activated): if (self.data.config.power_limit_condition == BatPowerLimitCondition.MANUAL.value or self.data.config.power_limit_condition == BatPowerLimitCondition.VEHICLE_CHARGING.value): return False @@ -625,7 +627,7 @@ def time_charging_min_bat_soc_allowed(self) -> bool: else: return False else: - return + return False return True diff --git a/packages/control/bat_all_test.py b/packages/control/bat_all_test.py index 414f7eac44..095bc3e9eb 100644 --- a/packages/control/bat_all_test.py +++ b/packages/control/bat_all_test.py @@ -372,25 +372,39 @@ def test_control_price_limit(params: BatControlParams, data_, monkeypatch): @pytest.mark.parametrize( "control_permitted, control_activated, condition, limit, expected_result", [ - pytest.param(False, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, True, + pytest.param(False, True, + BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, True, id="Speichersteuerung nicht erlaubt, aber aktiviert -> laden"), - pytest.param(True, False, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, True, + pytest.param(True, False, + BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, True, id="Speichersteuerung erlaubt, aber nicht aktiviert -> laden"), - pytest.param(True, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, + pytest.param(True, True, + BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, id="Manuell, volle Entladesperre -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, + pytest.param(True, True, + BatPowerLimitCondition.MANUAL.value, + BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, id="Manuell, Entladung in Fahrzeuge sperren -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value, False, + pytest.param(True, True, + BatPowerLimitCondition.MANUAL.value, BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value, False, id="Manuell, PV-Ertrag speichern -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, + pytest.param(True, True, + BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, id="Fahrzeuge laden, volle Entladesperre -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, + pytest.param(True, True, + BatPowerLimitCondition.VEHICLE_CHARGING.value, + BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, id="Fahrzeuge laden, Entladung in Fahrzeuge sperren -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.VEHICLE_CHARGING.value, BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value, False, + pytest.param(True, True, + BatPowerLimitCondition.VEHICLE_CHARGING.value, + BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value, False, id="Fahrzeuge laden, PV-Ertrag speichern -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.PRICE_LIMIT.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, + pytest.param(True, True, + BatPowerLimitCondition.PRICE_LIMIT.value, BatPowerLimitMode.MODE_NO_DISCHARGE.value, False, id="Preislimit, volle Entladesperre -> nicht laden"), - pytest.param(True, True, BatPowerLimitCondition.PRICE_LIMIT.value, BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, + pytest.param(True, True, + BatPowerLimitCondition.PRICE_LIMIT.value, + BatPowerLimitMode.MODE_DISCHARGE_HOME_CONSUMPTION.value, False, id="Preislimit, Entladung in Fahrzeuge sperren -> nicht laden"), ] @@ -402,6 +416,7 @@ def test_time_charging_min_bat_soc_allowed(control_permitted: bool, expected_result: bool): # setup b = BatAll() + b.data.config.configured = True b.data.config.power_limit_condition = condition b.data.config.power_limit_mode = limit b.data.config.bat_control_permitted = control_permitted @@ -431,14 +446,15 @@ def test_time_charging_min_bat_soc_allowed(control_permitted: bool, id="beide Strompreise deaktiviert -> nicht laden"), ] ) -def test_time_charging_min_bat_soc_allowed(ep_configured: bool, - price_limit_activated: bool, - price_charge_activated: bool, - price_threshold_mock: List[bool], - expected_result: bool, - monkeypatch): +def test_time_charging_min_bat_soc_allowed_pricing(ep_configured: bool, + price_limit_activated: bool, + price_charge_activated: bool, + price_threshold_mock: List[bool], + expected_result: bool, + monkeypatch): # setup b = BatAll() + b.data.config.configured = True b.data.config.power_limit_condition = BatPowerLimitCondition.PRICE_LIMIT.value b.data.config.power_limit_mode = BatPowerLimitMode.MODE_CHARGE_PV_PRODUCTION.value b.data.config.price_limit_activated = price_limit_activated diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index d4b5c05fcf..9d87b6fb95 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -171,12 +171,13 @@ def time_charging(self, elif plan.limit.selected == "amount" and used_amount_time_charging >= plan.limit.amount: # Energie-Limit erreicht message = self.TIME_CHARGING_AMOUNT_REACHED - elif plan.min_bat_soc is not None: + elif plan.min_bat_soc is not None and data.data.bat_all_data.data.config.configured: if data.data.bat_all_data.time_charging_min_bat_soc_allowed(): if data.data.bat_all_data.data.get.soc < plan.min_bat_soc: message = self.TIME_CHARGING_MIN_BAT_SOC_REACHED else: - log.debug("Zeitladen: minimaler Speicher-SoC überschritten, Laden mit Zeitladen möglich.") + log.debug( + "Zeitladen: minimaler Speicher-SoC überschritten, Laden mit Zeitladen möglich.") current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current sub_mode = "time_charging" else: diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 587d4eff06..96eb9ef81b 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -3078,4 +3078,4 @@ def upgrade(topic: str, payload) -> Optional[dict]: plan.update({"min_bat_soc": None}) return {topic: payload} self._loop_all_received_topics(upgrade) - # self._append_datastore_version(122) + self._append_datastore_version(122) From d6b4b4fed560012ada8a520d0de8d79e645a8e95 Mon Sep 17 00:00:00 2001 From: LKuemmel <76958050+LKuemmel@users.noreply.github.com> Date: Fri, 17 Apr 2026 14:50:49 +0200 Subject: [PATCH 4/4] Update packages/control/ev/charge_template.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/control/ev/charge_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index 9d87b6fb95..3135f9d15f 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -145,8 +145,8 @@ class ChargeTemplate: TIME_CHARGING_AMOUNT_REACHED = "Die gewünschte Energiemenge für das Zeitladen wurde geladen." TIME_CHARGING_CONFLICT_ACTIVE_BAT_CONTROL = ("Laden mit Zeitladen nach Speicher-SoC nicht möglich, da dies im " "Konflikt mit der aktiven Speichersteuerung steht.") - TIME_CHARGING_MIN_BAT_SOC_REACHED = ("Laden mit Zeitladen nach Speicher-SoC nicht möglich, da der minimale SoC des" - " Speichers erreicht wurde.") + TIME_CHARGING_MIN_BAT_SOC_REACHED = ("Laden mit Zeitladen nach Speicher-SoC nicht möglich, da der SoC des" + " Speichers unter dem minimalen SoC liegt.") def time_charging(self, soc: Optional[float],