From 575501d26ad867db5ebc1511504253176b7c8547 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 10 Oct 2022 09:28:36 +0200 Subject: [PATCH] Add select platform to LaMetric (#79803) --- homeassistant/components/lametric/const.py | 8 +- homeassistant/components/lametric/select.py | 91 +++++++++++++++++++ .../components/lametric/strings.select.json | 8 ++ .../lametric/translations/select.en.json | 8 ++ tests/components/lametric/test_select.py | 71 +++++++++++++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/lametric/select.py create mode 100644 homeassistant/components/lametric/strings.select.json create mode 100644 homeassistant/components/lametric/translations/select.en.json create mode 100644 tests/components/lametric/test_select.py diff --git a/homeassistant/components/lametric/const.py b/homeassistant/components/lametric/const.py index 6a3df3b54f1..ecaed7b833c 100644 --- a/homeassistant/components/lametric/const.py +++ b/homeassistant/components/lametric/const.py @@ -7,7 +7,13 @@ from typing import Final from homeassistant.const import Platform DOMAIN: Final = "lametric" -PLATFORMS = [Platform.BUTTON, Platform.NUMBER, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [ + Platform.BUTTON, + Platform.NUMBER, + Platform.SELECT, + Platform.SENSOR, + Platform.SWITCH, +] LOGGER = logging.getLogger(__package__) SCAN_INTERVAL = timedelta(seconds=30) diff --git a/homeassistant/components/lametric/select.py b/homeassistant/components/lametric/select.py new file mode 100644 index 00000000000..fccb6a3f771 --- /dev/null +++ b/homeassistant/components/lametric/select.py @@ -0,0 +1,91 @@ +"""Support for LaMetric selects.""" +from __future__ import annotations + +from collections.abc import Awaitable, Callable +from dataclasses import dataclass +from typing import Any + +from demetriek import BrightnessMode, Device, LaMetricDevice + +from homeassistant.components.select import SelectEntity, SelectEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import LaMetricDataUpdateCoordinator +from .entity import LaMetricEntity + + +@dataclass +class LaMetricEntityDescriptionMixin: + """Mixin values for LaMetric entities.""" + + options: list[str] + current_fn: Callable[[Device], str] + select_fn: Callable[[LaMetricDevice, str], Awaitable[Any]] + + +@dataclass +class LaMetricSelectEntityDescription( + SelectEntityDescription, LaMetricEntityDescriptionMixin +): + """Class describing LaMetric select entities.""" + + +SELECTS = [ + LaMetricSelectEntityDescription( + key="brightness_mode", + name="Brightness mode", + icon="mdi:brightness-auto", + entity_category=EntityCategory.CONFIG, + device_class="lametric__brightness_mode", + options=["auto", "manual"], + current_fn=lambda device: device.display.brightness_mode.value, + select_fn=lambda api, opt: api.display(brightness_mode=BrightnessMode(opt)), + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up LaMetric select based on a config entry.""" + coordinator: LaMetricDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + LaMetricSelectEntity( + coordinator=coordinator, + description=description, + ) + for description in SELECTS + ) + + +class LaMetricSelectEntity(LaMetricEntity, SelectEntity): + """Representation of a LaMetric select.""" + + entity_description: LaMetricSelectEntityDescription + + def __init__( + self, + coordinator: LaMetricDataUpdateCoordinator, + description: LaMetricSelectEntityDescription, + ) -> None: + """Initiate LaMetric Select.""" + super().__init__(coordinator) + self.entity_description = description + self._attr_options = description.options + self._attr_unique_id = f"{coordinator.data.serial_number}-{description.key}" + + @property + def current_option(self) -> str | None: + """Return the selected entity option to represent the entity state.""" + return self.entity_description.current_fn(self.coordinator.data) + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + await self.entity_description.select_fn(self.coordinator.lametric, option) + await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/lametric/strings.select.json b/homeassistant/components/lametric/strings.select.json new file mode 100644 index 00000000000..1d2ce0a2ce7 --- /dev/null +++ b/homeassistant/components/lametric/strings.select.json @@ -0,0 +1,8 @@ +{ + "state": { + "lametric__brightness_mode": { + "auto": "Automatic", + "manual": "Manual" + } + } +} diff --git a/homeassistant/components/lametric/translations/select.en.json b/homeassistant/components/lametric/translations/select.en.json new file mode 100644 index 00000000000..de1f7e5f642 --- /dev/null +++ b/homeassistant/components/lametric/translations/select.en.json @@ -0,0 +1,8 @@ +{ + "state": { + "lametric__brightness_mode": { + "auto": "Automatic", + "manual": "Manual" + } + } +} \ No newline at end of file diff --git a/tests/components/lametric/test_select.py b/tests/components/lametric/test_select.py new file mode 100644 index 00000000000..8d9394b9068 --- /dev/null +++ b/tests/components/lametric/test_select.py @@ -0,0 +1,71 @@ +"""Tests for the LaMetric select platform.""" +from unittest.mock import MagicMock + +from demetriek import BrightnessMode + +from homeassistant.components.lametric.const import DOMAIN +from homeassistant.components.select import ( + ATTR_OPTIONS, + DOMAIN as SELECT_DOMAIN, + SERVICE_SELECT_OPTION, +) +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + ATTR_ICON, + ATTR_OPTION, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.entity import EntityCategory + +from tests.common import MockConfigEntry + + +async def test_brightness_mode( + hass: HomeAssistant, + init_integration: MockConfigEntry, + mock_lametric: MagicMock, +) -> None: + """Test the LaMetric brightness mode controls.""" + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) + + state = hass.states.get("select.frenck_s_lametric_brightness_mode") + assert state + assert ( + state.attributes.get(ATTR_FRIENDLY_NAME) == "Frenck's LaMetric Brightness mode" + ) + assert state.attributes.get(ATTR_ICON) == "mdi:brightness-auto" + assert state.attributes.get(ATTR_OPTIONS) == ["auto", "manual"] + assert state.state == BrightnessMode.AUTO + + entry = entity_registry.async_get(state.entity_id) + assert entry + assert entry.device_id + assert entry.entity_category is EntityCategory.CONFIG + assert entry.unique_id == "SA110405124500W00BS9-brightness_mode" + + device = device_registry.async_get(entry.device_id) + assert device + assert device.configuration_url is None + assert device.connections == {(dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:dd:ee:ff")} + assert device.entry_type is None + assert device.hw_version is None + assert device.identifiers == {(DOMAIN, "SA110405124500W00BS9")} + assert device.manufacturer == "LaMetric Inc." + assert device.name == "Frenck's LaMetric" + assert device.sw_version == "2.2.2" + + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.frenck_s_lametric_brightness_mode", + ATTR_OPTION: "manual", + }, + blocking=True, + ) + + assert len(mock_lametric.display.mock_calls) == 1 + mock_lametric.display.assert_called_once_with(brightness_mode=BrightnessMode.MANUAL)