diff --git a/homeassistant/components/wolflink/sensor.py b/homeassistant/components/wolflink/sensor.py index 3ea65d26695..51cdcba193a 100644 --- a/homeassistant/components/wolflink/sensor.py +++ b/homeassistant/components/wolflink/sensor.py @@ -2,6 +2,9 @@ from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass + from wolf_comm.models import ( EnergyParameter, HoursParameter, @@ -10,6 +13,7 @@ from wolf_comm.models import ( PercentageParameter, PowerParameter, Pressure, + SimpleParameter, Temperature, ) @@ -55,70 +59,95 @@ async def async_setup_entry( async_add_entities(entities, True) -def get_entity_description(parameter: Parameter) -> SensorEntityDescription: +@dataclass(kw_only=True, frozen=True) +class WolflinkSensorEntityDescription(SensorEntityDescription): + """Describes Wolflink sensor entity.""" + + supported_fn: Callable[[Parameter], bool] + + +SENSOR_DESCRIPTIONS = [ + WolflinkSensorEntityDescription( + key="temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + supported_fn=lambda param: isinstance(param, Temperature), + ), + WolflinkSensorEntityDescription( + key="pressure", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=UnitOfPressure.BAR, + supported_fn=lambda param: isinstance(param, Pressure), + ), + WolflinkSensorEntityDescription( + key="energy", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + supported_fn=lambda param: isinstance(param, EnergyParameter), + ), + WolflinkSensorEntityDescription( + key="power", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.KILO_WATT, + supported_fn=lambda param: isinstance(param, PowerParameter), + ), + WolflinkSensorEntityDescription( + key="percentage", + native_unit_of_measurement=PERCENTAGE, + supported_fn=lambda param: isinstance(param, PercentageParameter), + ), + WolflinkSensorEntityDescription( + key="list_item", + translation_key="state", + supported_fn=lambda param: isinstance(param, ListItemParameter), + ), + WolflinkSensorEntityDescription( + key="hours", + icon="mdi:clock", + native_unit_of_measurement=UnitOfTime.HOURS, + supported_fn=lambda param: isinstance(param, HoursParameter), + ), + WolflinkSensorEntityDescription( + key="default", + supported_fn=lambda param: isinstance(param, SimpleParameter), + ), +] + + +def get_entity_description(parameter: Parameter) -> WolflinkSensorEntityDescription: """Return the entity description for a given parameter.""" - if isinstance(parameter, Temperature): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=UnitOfTemperature.CELSIUS, - ) - if isinstance(parameter, Pressure): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=UnitOfPressure.BAR, - ) - if isinstance(parameter, EnergyParameter): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, - ) - if isinstance(parameter, PowerParameter): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=UnitOfPower.KILO_WATT, - ) - if isinstance(parameter, PercentageParameter): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - native_unit_of_measurement=PERCENTAGE, - ) - if isinstance(parameter, ListItemParameter): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - translation_key="state", - ) - if isinstance(parameter, HoursParameter): - return SensorEntityDescription( - key=parameter.parameter_id, - name=parameter.name, - icon="mdi:clock", - native_unit_of_measurement=UnitOfTime.HOURS, - ) - return SensorEntityDescription( + for description in SENSOR_DESCRIPTIONS: + if description.supported_fn(parameter): + return WolflinkSensorEntityDescription( + key=parameter.parameter_id, + name=parameter.name, + device_class=description.device_class, + native_unit_of_measurement=description.native_unit_of_measurement, + icon=description.icon, + entity_registry_enabled_default=description.entity_registry_enabled_default, + entity_category=description.entity_category, + has_entity_name=description.has_entity_name, + translation_key=description.translation_key, + supported_fn=description.supported_fn, + ) + return WolflinkSensorEntityDescription( key=parameter.parameter_id, name=parameter.name, + supported_fn=lambda param: True, ) class WolfLinkSensor(CoordinatorEntity, SensorEntity): """Base class for all Wolf entities.""" + entity_description: WolflinkSensorEntityDescription + def __init__( self, coordinator, wolf_object: Parameter, device_id: str, - description: SensorEntityDescription, + description: WolflinkSensorEntityDescription, ) -> None: """Initialize.""" super().__init__(coordinator) @@ -162,10 +191,3 @@ class WolfLinkSensor(CoordinatorEntity, SensorEntity): "value_id": self.wolf_object.value_id, "parent": self.wolf_object.parent, } - - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit the value is expressed in.""" - if isinstance(self.wolf_object, PercentageParameter): - return self.wolf_object.unit - return self.entity_description.native_unit_of_measurement diff --git a/tests/components/wolflink/test_sensor.py b/tests/components/wolflink/test_sensor.py index a3b223f0145..b1a0425fc68 100644 --- a/tests/components/wolflink/test_sensor.py +++ b/tests/components/wolflink/test_sensor.py @@ -1,16 +1,18 @@ """Test the Wolf SmartSet Service Sensor platform.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, Mock import pytest from wolf_comm import ( PERCENTAGE, EnergyParameter, HoursParameter, + ListItemParameter, Parameter, PercentageParameter, PowerParameter, Pressure, + SimpleParameter, Temperature, ) @@ -21,7 +23,11 @@ from homeassistant.components.wolflink.const import ( MANUFACTURER, PARAMETERS, ) -from homeassistant.components.wolflink.sensor import WolfLinkSensor, async_setup_entry +from homeassistant.components.wolflink.sensor import ( + WolfLinkSensor, + async_setup_entry, + get_entity_description, +) from homeassistant.const import ( UnitOfEnergy, UnitOfPower, @@ -34,7 +40,7 @@ from homeassistant.helpers import device_registry as dr from .const import CONFIG -from tests.common import MockConfigEntry +from tests.common import Literal, MockConfigEntry @pytest.fixture @@ -46,6 +52,18 @@ def mock_coordinator(hass: HomeAssistant) -> MagicMock: return coordinator +@pytest.fixture +def mock_device_id(): + """Fixture for a mock device ID.""" + return "1234" + + +@pytest.fixture +def mock_parameter(): + """Fixture for a mock parameter.""" + return Mock(spec=Parameter) + + async def mock_config_entry( hass: HomeAssistant, device_registry: dr.DeviceRegistry ) -> None: @@ -55,13 +73,9 @@ async def mock_config_entry( ) config_entry.add_to_hass(hass) - device_id = device_registry.async_get_or_create( - config_entry_id=config_entry.entry_id, - identifiers={(DOMAIN, CONFIG[DEVICE_ID])}, - configuration_url="https://www.wolf-smartset.com/", - manufacturer=MANUFACTURER, - ).id - assert device_registry.async_get(device_id).identifiers == {(DOMAIN, "1234")} + device = device_registry.async_get_device({(DOMAIN, CONFIG[DEVICE_ID])}) + assert device is not None + assert device.identifiers == {(DOMAIN, CONFIG[DEVICE_ID])} def test_wolflink_sensor_native_value(mock_coordinator: MagicMock) -> None: @@ -117,7 +131,7 @@ async def test_async_setup_entry( hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = { PARAMETERS: [parameter], COORDINATOR: mock_coordinator, - DEVICE_ID: "mock_device_id", + DEVICE_ID: "1234", } async_add_entities = MagicMock() @@ -129,3 +143,74 @@ async def test_async_setup_entry( entity = entities[0] assert isinstance(entity, expected_class) assert entity.native_unit_of_measurement == expected_unit + + +def test_get_entity_description() -> None: + """Test the get_entity_description function.""" + parameter = Mock(spec=Temperature) + description = get_entity_description(parameter) + assert description.device_class == "temperature" + assert description.native_unit_of_measurement == "°C" + + parameter = Mock(spec=Pressure) + description = get_entity_description(parameter) + assert description.device_class == "pressure" + assert description.native_unit_of_measurement == "bar" + + parameter = Mock(spec=EnergyParameter) + description = get_entity_description(parameter) + assert description.device_class == "energy" + assert description.native_unit_of_measurement == "kWh" + + parameter = Mock(spec=PowerParameter) + description = get_entity_description(parameter) + assert description.device_class == "power" + assert description.native_unit_of_measurement == "kW" + + parameter = Mock(spec=PercentageParameter) + description = get_entity_description(parameter) + assert description.native_unit_of_measurement == PERCENTAGE + + parameter = Mock(spec=ListItemParameter) + description = get_entity_description(parameter) + assert description.translation_key == "state" + + parameter = Mock(spec=HoursParameter) + description = get_entity_description(parameter) + assert description.native_unit_of_measurement == UnitOfTime.HOURS + assert description.icon == "mdi:clock" + + parameter = Mock(spec=SimpleParameter) + description = get_entity_description(parameter) + + +def test_wolflink_sensor( + mock_coordinator, mock_device_id: Literal["1234"], mock_parameter +) -> None: + """Test the WolfLinkSensor class.""" + description = get_entity_description(mock_parameter) + sensor = WolfLinkSensor( + mock_coordinator, mock_parameter, mock_device_id, description + ) + + assert sensor.entity_description == description + assert sensor.wolf_object == mock_parameter + assert sensor._attr_name == description.name + assert sensor._attr_unique_id == f"{mock_device_id}:{mock_parameter.parameter_id}" + assert sensor._attr_device_info["identifiers"] == {(DOMAIN, str(mock_device_id))} + assert ( + sensor._attr_device_info["configuration_url"] + == "https://www.wolf-smartset.com/" + ) + assert sensor._attr_device_info["manufacturer"] == MANUFACTURER + + # Test native_value property + mock_coordinator.data = {mock_parameter.parameter_id: ("value_id", "state")} + assert sensor.native_value == "state" + + # Test extra_state_attributes property + assert sensor.extra_state_attributes == { + "parameter_id": mock_parameter.parameter_id, + "value_id": mock_parameter.value_id, + "parent": mock_parameter.parent, + }