From 84c20745a847fe92fb66bcb9959a5e968886442f Mon Sep 17 00:00:00 2001 From: "Lektri.co" <137074859+Lektrico@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:30:24 +0300 Subject: [PATCH] Add number platform to the Lektrico integration (#126119) * Add platform number. * Remove number user_limit. * Change LED to led in number snapshot. * Update homeassistant/components/lektrico/number.py --------- Co-authored-by: Joost Lekkerkerker --- homeassistant/components/lektrico/__init__.py | 6 +- homeassistant/components/lektrico/number.py | 100 ++++++++++++++++ .../components/lektrico/strings.json | 8 ++ .../lektrico/fixtures/get_info.json | 5 +- .../lektrico/snapshots/test_number.ambr | 113 ++++++++++++++++++ tests/components/lektrico/test_number.py | 31 +++++ 6 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/lektrico/number.py create mode 100644 tests/components/lektrico/snapshots/test_number.ambr create mode 100644 tests/components/lektrico/test_number.py diff --git a/homeassistant/components/lektrico/__init__.py b/homeassistant/components/lektrico/__init__.py index 746d14f3605..bd2ca8de214 100644 --- a/homeassistant/components/lektrico/__init__.py +++ b/homeassistant/components/lektrico/__init__.py @@ -11,7 +11,11 @@ from homeassistant.core import HomeAssistant from .coordinator import LektricoDeviceDataUpdateCoordinator # List the platforms that charger supports. -CHARGERS_PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.SENSOR] +CHARGERS_PLATFORMS: list[Platform] = [ + Platform.BUTTON, + Platform.NUMBER, + Platform.SENSOR, +] # List the platforms that load balancer device supports. LB_DEVICES_PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.SENSOR] diff --git a/homeassistant/components/lektrico/number.py b/homeassistant/components/lektrico/number.py new file mode 100644 index 00000000000..8054ba8afe5 --- /dev/null +++ b/homeassistant/components/lektrico/number.py @@ -0,0 +1,100 @@ +"""Support for Lektrico number entities.""" + +from collections.abc import Callable, Coroutine +from dataclasses import dataclass +from typing import Any + +from lektricowifi import Device + +from homeassistant.components.number import NumberEntity, NumberEntityDescription +from homeassistant.const import ( + ATTR_SERIAL_NUMBER, + CONF_TYPE, + PERCENTAGE, + EntityCategory, + UnitOfElectricCurrent, +) +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 LektricoNumberEntityDescription(NumberEntityDescription): + """Describes Lektrico number entity.""" + + value_fn: Callable[[dict[str, Any]], int] + set_value_fn: Callable[[Device, int], Coroutine[Any, Any, dict[Any, Any]]] + + +NUMBERS: tuple[LektricoNumberEntityDescription, ...] = ( + LektricoNumberEntityDescription( + key="led_max_brightness", + translation_key="led_max_brightness", + entity_category=EntityCategory.CONFIG, + native_min_value=0, + native_max_value=100, + native_step=5, + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda data: int(data["led_max_brightness"]), + set_value_fn=lambda data, value: data.set_led_max_brightness(value), + ), + LektricoNumberEntityDescription( + key="dynamic_limit", + translation_key="dynamic_limit", + entity_category=EntityCategory.CONFIG, + native_min_value=0, + native_max_value=32, + native_step=1, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + value_fn=lambda data: int(data["dynamic_current"]), + set_value_fn=lambda data, value: data.set_dynamic_current(value), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: LektricoConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Lektrico number entities based on a config entry.""" + coordinator = entry.runtime_data + + async_add_entities( + LektricoNumber( + description, + coordinator, + f"{entry.data[CONF_TYPE]}_{entry.data[ATTR_SERIAL_NUMBER]}", + ) + for description in NUMBERS + ) + + +class LektricoNumber(LektricoEntity, NumberEntity): + """Defines a Lektrico number entity.""" + + entity_description: LektricoNumberEntityDescription + + def __init__( + self, + description: LektricoNumberEntityDescription, + coordinator: LektricoDeviceDataUpdateCoordinator, + device_name: str, + ) -> None: + """Initialize Lektrico number.""" + super().__init__(coordinator, device_name) + self.entity_description = description + self._attr_unique_id = f"{coordinator.serial_number}_{description.key}" + + @property + def native_value(self) -> int | None: + """Return the state of the number.""" + return self.entity_description.value_fn(self.coordinator.data) + + async def async_set_native_value(self, value: float) -> None: + """Set the selected value.""" + await self.entity_description.set_value_fn(self.coordinator.device, int(value)) + await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/lektrico/strings.json b/homeassistant/components/lektrico/strings.json index 2470c0865d5..a636ee543e6 100644 --- a/homeassistant/components/lektrico/strings.json +++ b/homeassistant/components/lektrico/strings.json @@ -30,6 +30,14 @@ "name": "Charge stop" } }, + "number": { + "led_max_brightness": { + "name": "Led brightness" + }, + "dynamic_limit": { + "name": "Dynamic limit" + } + }, "sensor": { "state": { "name": "State", diff --git a/tests/components/lektrico/fixtures/get_info.json b/tests/components/lektrico/fixtures/get_info.json index a8f2a56b8d8..7c2fc30b0b0 100644 --- a/tests/components/lektrico/fixtures/get_info.json +++ b/tests/components/lektrico/fixtures/get_info.json @@ -9,5 +9,8 @@ "current_limit_reason": "installation_current", "voltage_l1": 220.0, "current_l1": 0.0, - "fw_version": "1.44" + "fw_version": "1.44", + "led_max_brightness": 20, + "dynamic_current": 32, + "user_current": 32 } diff --git a/tests/components/lektrico/snapshots/test_number.ambr b/tests/components/lektrico/snapshots/test_number.ambr new file mode 100644 index 00000000000..30a37a25a09 --- /dev/null +++ b/tests/components/lektrico/snapshots/test_number.ambr @@ -0,0 +1,113 @@ +# serializer version: 1 +# name: test_all_entities[number.1p7k_500006_dynamic_limit-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 32, + 'min': 0, + 'mode': , + 'step': 1, + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.1p7k_500006_dynamic_limit', + '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': 'Dynamic limit', + 'platform': 'lektrico', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'dynamic_limit', + 'unique_id': '500006_dynamic_limit', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[number.1p7k_500006_dynamic_limit-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': '1p7k_500006 Dynamic limit', + 'max': 32, + 'min': 0, + 'mode': , + 'step': 1, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.1p7k_500006_dynamic_limit', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '32', + }) +# --- +# name: test_all_entities[number.1p7k_500006_led_brightness-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 100, + 'min': 0, + 'mode': , + 'step': 5, + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.1p7k_500006_led_brightness', + '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': 'Led brightness', + 'platform': 'lektrico', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'led_max_brightness', + 'unique_id': '500006_led_max_brightness', + 'unit_of_measurement': '%', + }) +# --- +# name: test_all_entities[number.1p7k_500006_led_brightness-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': '1p7k_500006 Led brightness', + 'max': 100, + 'min': 0, + 'mode': , + 'step': 5, + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'number.1p7k_500006_led_brightness', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- diff --git a/tests/components/lektrico/test_number.py b/tests/components/lektrico/test_number.py new file mode 100644 index 00000000000..ade6515ca72 --- /dev/null +++ b/tests/components/lektrico/test_number.py @@ -0,0 +1,31 @@ +"""Tests for the Lektrico number 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.NUMBER], + LB_DEVICES_PLATFORMS=[Platform.NUMBER], + ): + await setup_integration(hass, mock_config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)