From 4ac9b339a1cad0a2abfbf62b5592609c58fd57e3 Mon Sep 17 00:00:00 2001 From: "Lektri.co" <137074859+Lektrico@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:08:28 +0300 Subject: [PATCH] Add select platform to the Lektrico integration (#126490) * Add select for Lektrico integration. * Rename lb_mode to load_balancing_mode. * Update homeassistant/components/lektrico/strings.json --------- Co-authored-by: Joost Lekkerkerker --- homeassistant/components/lektrico/__init__.py | 6 +- homeassistant/components/lektrico/select.py | 91 +++++++++++++++++++ .../components/lektrico/strings.json | 11 +++ .../lektrico/fixtures/get_info.json | 3 +- .../lektrico/snapshots/test_select.ambr | 60 ++++++++++++ tests/components/lektrico/test_select.py | 31 +++++++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/lektrico/select.py create mode 100644 tests/components/lektrico/snapshots/test_select.ambr create mode 100644 tests/components/lektrico/test_select.py diff --git a/homeassistant/components/lektrico/__init__.py b/homeassistant/components/lektrico/__init__.py index bd2ca8de214..0691bfef72a 100644 --- a/homeassistant/components/lektrico/__init__.py +++ b/homeassistant/components/lektrico/__init__.py @@ -18,7 +18,11 @@ CHARGERS_PLATFORMS: list[Platform] = [ ] # List the platforms that load balancer device supports. -LB_DEVICES_PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.SENSOR] +LB_DEVICES_PLATFORMS: list[Platform] = [ + Platform.BUTTON, + Platform.SELECT, + Platform.SENSOR, +] type LektricoConfigEntry = ConfigEntry[LektricoDeviceDataUpdateCoordinator] diff --git a/homeassistant/components/lektrico/select.py b/homeassistant/components/lektrico/select.py new file mode 100644 index 00000000000..ef45d97d697 --- /dev/null +++ b/homeassistant/components/lektrico/select.py @@ -0,0 +1,91 @@ +"""Support for Lektrico select entities.""" + +from collections.abc import Callable, Coroutine +from dataclasses import dataclass +from typing import Any + +from lektricowifi import Device + +from homeassistant.components.select import SelectEntity, SelectEntityDescription +from homeassistant.const import ATTR_SERIAL_NUMBER, CONF_TYPE, EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import LektricoConfigEntry, LektricoDeviceDataUpdateCoordinator +from .entity import LektricoEntity + + +@dataclass(frozen=True, kw_only=True) +class LektricoSelectEntityDescription(SelectEntityDescription): + """Describes Lektrico select entity.""" + + value_fn: Callable[[dict[str, Any]], str] + set_value_fn: Callable[[Device, int], Coroutine[Any, Any, dict[Any, Any]]] + + +LB_MODE_OPTIONS = [ + "disabled", + "power", + "hybrid", + "green", +] + + +SELECTS: tuple[LektricoSelectEntityDescription, ...] = ( + LektricoSelectEntityDescription( + key="load_balancing_mode", + translation_key="load_balancing_mode", + options=LB_MODE_OPTIONS, + entity_category=EntityCategory.CONFIG, + value_fn=lambda data: LB_MODE_OPTIONS[data["lb_mode"]], + set_value_fn=lambda device, value: device.set_load_balancing_mode(value), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: LektricoConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Lektrico select entities based on a config entry.""" + + coordinator = entry.runtime_data + + async_add_entities( + LektricoSelect( + description, + coordinator, + f"{entry.data[CONF_TYPE]}_{entry.data[ATTR_SERIAL_NUMBER]}", + ) + for description in SELECTS + ) + + +class LektricoSelect(LektricoEntity, SelectEntity): + """Defines a Lektrico select entity.""" + + entity_description: LektricoSelectEntityDescription + + def __init__( + self, + description: LektricoSelectEntityDescription, + coordinator: LektricoDeviceDataUpdateCoordinator, + device_name: str, + ) -> None: + """Initialize Lektrico select.""" + super().__init__(coordinator, device_name) + self.entity_description = description + self._attr_unique_id = f"{coordinator.serial_number}_{description.key}" + + @property + def current_option(self) -> str | None: + """Return the state of the select.""" + return self.entity_description.value_fn(self.coordinator.data) + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + await self.entity_description.set_value_fn( + self.coordinator.device, LB_MODE_OPTIONS.index(option) + ) + await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/lektrico/strings.json b/homeassistant/components/lektrico/strings.json index 3f4a732a4a0..b749ea23490 100644 --- a/homeassistant/components/lektrico/strings.json +++ b/homeassistant/components/lektrico/strings.json @@ -38,6 +38,17 @@ "name": "Dynamic limit" } }, + "select": { + "load_balancing_mode": { + "name": "Load balancing mode", + "state": { + "disabled": "[%key:common::state::disabled%]", + "power": "Power", + "hybrid": "Hybrid", + "green": "Green" + } + } + }, "sensor": { "state": { "name": "State", diff --git a/tests/components/lektrico/fixtures/get_info.json b/tests/components/lektrico/fixtures/get_info.json index 7c2fc30b0b0..2f190d2f00c 100644 --- a/tests/components/lektrico/fixtures/get_info.json +++ b/tests/components/lektrico/fixtures/get_info.json @@ -12,5 +12,6 @@ "fw_version": "1.44", "led_max_brightness": 20, "dynamic_current": 32, - "user_current": 32 + "user_current": 32, + "lb_mode": 0 } diff --git a/tests/components/lektrico/snapshots/test_select.ambr b/tests/components/lektrico/snapshots/test_select.ambr new file mode 100644 index 00000000000..5a964f52ada --- /dev/null +++ b/tests/components/lektrico/snapshots/test_select.ambr @@ -0,0 +1,60 @@ +# serializer version: 1 +# name: test_all_entities[select.1p7k_500006_load_balancing_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'disabled', + 'power', + 'hybrid', + 'green', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.1p7k_500006_load_balancing_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Load balancing mode', + 'platform': 'lektrico', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'load_balancing_mode', + 'unique_id': '500006_load_balancing_mode', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[select.1p7k_500006_load_balancing_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': '1p7k_500006 Load balancing mode', + 'options': list([ + 'disabled', + 'power', + 'hybrid', + 'green', + ]), + }), + 'context': , + 'entity_id': 'select.1p7k_500006_load_balancing_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'disabled', + }) +# --- diff --git a/tests/components/lektrico/test_select.py b/tests/components/lektrico/test_select.py new file mode 100644 index 00000000000..cb09c47535e --- /dev/null +++ b/tests/components/lektrico/test_select.py @@ -0,0 +1,31 @@ +"""Tests for the Lektrico select platform.""" + +from unittest.mock import AsyncMock, patch + +from syrupy import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_device: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + with patch.multiple( + "homeassistant.components.lektrico", + CHARGERS_PLATFORMS=[Platform.SELECT], + LB_DEVICES_PLATFORMS=[Platform.SELECT], + ): + await setup_integration(hass, mock_config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)