From 036cebbf06d179444d5567ce2e5b7e50b51af0c6 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 29 Aug 2026 01:50:01 +0200 Subject: [PATCH] Only offer smart charging modes the Peblar accepts (#180565) --- homeassistant/components/peblar/select.py | 38 ++++++++++++++++++----- tests/components/peblar/test_select.py | 36 +++++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/peblar/select.py b/homeassistant/components/peblar/select.py index 5f2720826254..37a4f491130c 100644 --- a/homeassistant/components/peblar/select.py +++ b/homeassistant/components/peblar/select.py @@ -33,22 +33,38 @@ class PeblarSelectEntityDescription(SelectEntityDescription): """Class describing Peblar select entities.""" has_fn: Callable[[PeblarRuntimeData], bool] = lambda _: True + options_fn: Callable[[PeblarUserConfiguration], list[str]] | None = None current_fn: Callable[[PeblarUserConfiguration], str | None] select_fn: Callable[[Peblar, str], Awaitable[Any]] +def _smart_charging_options(configuration: PeblarUserConfiguration) -> list[str]: + """Return the smart charging modes this charger will accept. + + A charger without a power meter configured rejects solar charging, and + scheduled charging can be switched off during commissioning. Offering + those anyway lands the user on a mode the charger quietly ignores. + """ + solar = configuration.solar_charging_allowed + return [ + option + for option, allowed in ( + ("default", True), + ("fast_solar", solar), + ("pure_solar", solar), + ("scheduled", configuration.scheduled_charging_allowed), + ("smart_solar", solar), + ) + if allowed + ] + + DESCRIPTIONS = [ PeblarSelectEntityDescription( key="smart_charging", translation_key="smart_charging", entity_category=EntityCategory.CONFIG, - options=[ - "default", - "fast_solar", - "pure_solar", - "scheduled", - "smart_solar", - ], + options_fn=_smart_charging_options, current_fn=lambda x: x.smart_charging.value if x.smart_charging else None, select_fn=lambda x, mode: x.smart_charging(SmartChargingMode(mode)), ), @@ -118,6 +134,14 @@ class PeblarSelectEntity( entity_description: PeblarSelectEntityDescription + @property + @override + def options(self) -> list[str]: + """Return the options this charger currently accepts.""" + if (options_fn := self.entity_description.options_fn) is not None: + return options_fn(self.coordinator.data) + return super().options + @property @override def current_option(self) -> str | None: diff --git a/tests/components/peblar/test_select.py b/tests/components/peblar/test_select.py index 6c7ef209347a..8c3d8b045974 100644 --- a/tests/components/peblar/test_select.py +++ b/tests/components/peblar/test_select.py @@ -17,6 +17,7 @@ from syrupy.assertion import SnapshotAssertion from homeassistant.components.peblar.const import DOMAIN from homeassistant.components.select import ( ATTR_OPTION, + ATTR_OPTIONS, DOMAIN as SELECT_DOMAIN, SERVICE_SELECT_OPTION, ) @@ -250,3 +251,38 @@ async def test_hw_entity_absent_when_hw_flag_false( ) is None ) + + +@pytest.mark.parametrize( + ("mock_peblar", "expected_options"), + [ + ( + {"SolarChargingAllowed": False}, + ["default", "scheduled"], + ), + ( + {"ScheduledChargingAllowed": False}, + ["default", "fast_solar", "pure_solar", "smart_solar"], + ), + ( + {"SolarChargingAllowed": False, "ScheduledChargingAllowed": False}, + ["default"], + ), + ], + ids=["no solar", "no scheduled", "neither"], + indirect=["mock_peblar"], +) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_smart_charging_options_follow_the_charger( + hass: HomeAssistant, + expected_options: list[str], +) -> None: + """Only offer the smart charging modes the charger accepts. + + A charger without a power meter rejects solar charging, and the web + interface hides those modes. Offering them anyway lets the user pick + something the charger quietly ignores. + """ + state = hass.states.get("select.peblar_ev_charger_smart_charging") + assert state + assert state.attributes[ATTR_OPTIONS] == expected_options