diff --git a/homeassistant/components/wolflink/sensor.py b/homeassistant/components/wolflink/sensor.py index 84939df8cb8..3ea65d26695 100644 --- a/homeassistant/components/wolflink/sensor.py +++ b/homeassistant/components/wolflink/sensor.py @@ -10,13 +10,17 @@ from wolf_comm.models import ( PercentageParameter, PowerParameter, Pressure, - SimpleParameter, Temperature, ) -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + PERCENTAGE, UnitOfEnergy, UnitOfPower, UnitOfPressure, @@ -37,41 +41,90 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up all entries for Wolf Platform.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR] parameters = hass.data[DOMAIN][config_entry.entry_id][PARAMETERS] device_id = hass.data[DOMAIN][config_entry.entry_id][DEVICE_ID] - entities: list[WolfLinkSensor] = [] - for parameter in parameters: - if isinstance(parameter, Temperature): - entities.append(WolfLinkTemperature(coordinator, parameter, device_id)) - if isinstance(parameter, Pressure): - entities.append(WolfLinkPressure(coordinator, parameter, device_id)) - if isinstance(parameter, EnergyParameter): - entities.append(WolfLinkEnergy(coordinator, parameter, device_id)) - if isinstance(parameter, PowerParameter): - entities.append(WolfLinkPower(coordinator, parameter, device_id)) - if isinstance(parameter, PercentageParameter): - entities.append(WolfLinkPercentage(coordinator, parameter, device_id)) - if isinstance(parameter, ListItemParameter): - entities.append(WolfLinkState(coordinator, parameter, device_id)) - if isinstance(parameter, HoursParameter): - entities.append(WolfLinkHours(coordinator, parameter, device_id)) - if isinstance(parameter, SimpleParameter): - entities.append(WolfLinkSensor(coordinator, parameter, device_id)) + entities: list[WolfLinkSensor] = [ + WolfLinkSensor( + coordinator, parameter, device_id, get_entity_description(parameter) + ) + for parameter in parameters + ] async_add_entities(entities, True) +def get_entity_description(parameter: Parameter) -> SensorEntityDescription: + """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( + key=parameter.parameter_id, + name=parameter.name, + ) + + class WolfLinkSensor(CoordinatorEntity, SensorEntity): """Base class for all Wolf entities.""" - def __init__(self, coordinator, wolf_object: Parameter, device_id) -> None: + def __init__( + self, + coordinator, + wolf_object: Parameter, + device_id: str, + description: SensorEntityDescription, + ) -> None: """Initialize.""" super().__init__(coordinator) + self.entity_description = description self.wolf_object = wolf_object - self._attr_name = wolf_object.name + self._attr_name = str(description.name) self._attr_unique_id = f"{device_id}:{wolf_object.parameter_id}" self._state = None self._attr_device_info = DeviceInfo( @@ -81,16 +134,28 @@ class WolfLinkSensor(CoordinatorEntity, SensorEntity): ) @property - def native_value(self): + def native_value(self) -> str | None: """Return the state. Wolf Client is returning only changed values so we need to store old value here.""" if self.wolf_object.parameter_id in self.coordinator.data: new_state = self.coordinator.data[self.wolf_object.parameter_id] self.wolf_object.value_id = new_state[0] self._state = new_state[1] + if ( + isinstance(self.wolf_object, ListItemParameter) + and self._state is not None + ): + resolved_state = [ + item + for item in self.wolf_object.items + if item.value == int(self._state) + ] + if resolved_state: + resolved_name = resolved_state[0].name + self._state = STATES.get(resolved_name, resolved_name) return self._state @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, str | None]: """Return the state attributes.""" return { "parameter_id": self.wolf_object.parameter_id, @@ -98,65 +163,9 @@ class WolfLinkSensor(CoordinatorEntity, SensorEntity): "parent": self.wolf_object.parent, } - -class WolfLinkHours(WolfLinkSensor): - """Class for hour based entities.""" - - _attr_icon = "mdi:clock" - _attr_native_unit_of_measurement = UnitOfTime.HOURS - - -class WolfLinkTemperature(WolfLinkSensor): - """Class for temperature based entities.""" - - _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS - - -class WolfLinkPressure(WolfLinkSensor): - """Class for pressure based entities.""" - - _attr_device_class = SensorDeviceClass.PRESSURE - _attr_native_unit_of_measurement = UnitOfPressure.BAR - - -class WolfLinkPower(WolfLinkSensor): - """Class for power based entities.""" - - _attr_device_class = SensorDeviceClass.POWER - _attr_native_unit_of_measurement = UnitOfPower.KILO_WATT - - -class WolfLinkEnergy(WolfLinkSensor): - """Class for energy based entities.""" - - _attr_device_class = SensorDeviceClass.ENERGY - _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR - - -class WolfLinkPercentage(WolfLinkSensor): - """Class for percentage based entities.""" - @property - def native_unit_of_measurement(self): + def native_unit_of_measurement(self) -> str | None: """Return the unit the value is expressed in.""" - return self.wolf_object.unit - - -class WolfLinkState(WolfLinkSensor): - """Class for entities which has defined list of state.""" - - _attr_translation_key = "state" - - @property - def native_value(self): - """Return the state converting with supported values.""" - state = super().native_value - if state is not None: - resolved_state = [ - item for item in self.wolf_object.items if item.value == int(state) - ] - if resolved_state: - resolved_name = resolved_state[0].name - return STATES.get(resolved_name, resolved_name) - return state + 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 df2b3f47513..a3b223f0145 100644 --- a/tests/components/wolflink/test_sensor.py +++ b/tests/components/wolflink/test_sensor.py @@ -3,7 +3,16 @@ from unittest.mock import MagicMock import pytest -from wolf_comm import EnergyParameter, PowerParameter +from wolf_comm import ( + PERCENTAGE, + EnergyParameter, + HoursParameter, + Parameter, + PercentageParameter, + PowerParameter, + Pressure, + Temperature, +) from homeassistant.components.wolflink.const import ( COORDINATOR, @@ -12,18 +21,8 @@ from homeassistant.components.wolflink.const import ( MANUFACTURER, PARAMETERS, ) -from homeassistant.components.wolflink.sensor import ( - WolfLinkEnergy, - WolfLinkHours, - WolfLinkPercentage, - WolfLinkPower, - WolfLinkPressure, - WolfLinkSensor, - WolfLinkTemperature, - async_setup_entry, -) +from homeassistant.components.wolflink.sensor import WolfLinkSensor, async_setup_entry from homeassistant.const import ( - PERCENTAGE, UnitOfEnergy, UnitOfPower, UnitOfPressure, @@ -69,7 +68,7 @@ def test_wolflink_sensor_native_value(mock_coordinator: MagicMock) -> None: """Test WolflinkSensor native value.""" parameter = MagicMock() parameter.parameter_id = "outside_temp" - sensor = WolfLinkSensor(mock_coordinator, parameter, "mock_device_id") + sensor = WolfLinkSensor(mock_coordinator, parameter, "mock_device_id", MagicMock()) mock_coordinator.data = {"outside_temp": [None, 20]} assert sensor.native_value == 20 @@ -80,85 +79,41 @@ def test_wolflink_sensor_extra_state_attributes(mock_coordinator: MagicMock) -> parameter.parameter_id = "outside_temp" parameter.value_id = "value_id" parameter.parent = "parent" - sensor = WolfLinkSensor(mock_coordinator, parameter, "mock_device_id") + sensor = WolfLinkSensor(mock_coordinator, parameter, "mock_device_id", MagicMock()) attributes = sensor.extra_state_attributes assert attributes["parameter_id"] == "outside_temp" assert attributes["value_id"] == "value_id" assert attributes["parent"] == "parent" -def test_wolflink_temperature_initialization(mock_coordinator: MagicMock) -> None: - """Test WolflinkTemperature initialization.""" - parameter = MagicMock() - parameter.name = "Outside Temperature" - parameter.parameter_id = "outside_temp" - sensor = WolfLinkTemperature(mock_coordinator, parameter, "mock_device_id") - assert sensor._attr_device_class == "temperature" - assert sensor._attr_native_unit_of_measurement == UnitOfTemperature.CELSIUS - - -def test_wolflink_pressure_initialization(mock_coordinator: MagicMock) -> None: - """Test WolflinkPressure initialization.""" - parameter = MagicMock() - parameter.name = "Pressure" - parameter.parameter_id = "pressure" - sensor = WolfLinkPressure(mock_coordinator, parameter, "mock_device_id") - assert sensor._attr_device_class == "pressure" - assert sensor._attr_native_unit_of_measurement == UnitOfPressure.BAR - - -def test_wolflink_power_initialization(mock_coordinator: MagicMock) -> None: - """Test WolflinkPower initialization.""" - parameter = MagicMock() - parameter.name = "Power" - parameter.parameter_id = "power" - sensor = WolfLinkPower(mock_coordinator, parameter, "mock_device_id") - assert sensor._attr_device_class == "power" - assert sensor._attr_native_unit_of_measurement == UnitOfPower.KILO_WATT - - -def test_wolflink_energy_initialization(mock_coordinator: MagicMock) -> None: - """Test WolflinkEnergy initialization.""" - parameter = MagicMock() - parameter.name = "Energy" - parameter.parameter_id = "energy" - sensor = WolfLinkEnergy(mock_coordinator, parameter, "mock_device_id") - assert sensor._attr_device_class == "energy" - assert sensor._attr_native_unit_of_measurement == UnitOfEnergy.KILO_WATT_HOUR - - -def test_wolflink_percentage_initialization(mock_coordinator: MagicMock) -> None: - """Test WolflinkPercentage initialization.""" - parameter = MagicMock() - parameter.name = "Percentage" - parameter.parameter_id = "percentage" - parameter.unit = PERCENTAGE - sensor = WolfLinkPercentage(mock_coordinator, parameter, "mock_device_id") - assert sensor.native_unit_of_measurement == PERCENTAGE - - -def test_wolflink_hours_initialization(mock_coordinator: MagicMock) -> None: - """Test WolflinkHours initialization.""" - parameter = MagicMock() - parameter.name = "Hours" - parameter.parameter_id = "hours" - sensor = WolfLinkHours(mock_coordinator, parameter, "mock_device_id") - assert sensor._attr_icon == "mdi:clock" - assert sensor._attr_native_unit_of_measurement == UnitOfTime.HOURS - - -async def test_async_setup_entry_wolflink_energy( - hass: HomeAssistant, mock_coordinator: MagicMock +@pytest.mark.parametrize( + ("parameter", "expected_class", "expected_unit"), + [ + (MagicMock(spec=EnergyParameter), WolfLinkSensor, UnitOfEnergy.KILO_WATT_HOUR), + (MagicMock(spec=PowerParameter), WolfLinkSensor, UnitOfPower.KILO_WATT), + (MagicMock(spec=Pressure), WolfLinkSensor, UnitOfPressure.BAR), + (MagicMock(spec=Temperature), WolfLinkSensor, UnitOfTemperature.CELSIUS), + (MagicMock(spec=PercentageParameter), WolfLinkSensor, PERCENTAGE), + (MagicMock(spec=HoursParameter), WolfLinkSensor, UnitOfTime.HOURS), + ], +) +async def test_async_setup_entry( + hass: HomeAssistant, + mock_coordinator: MagicMock, + parameter: Parameter, + expected_class: type, + expected_unit: str, ) -> None: - """Test async_setup_entry for WolfLinkEnergy.""" + """Test async_setup_entry for various parameter types.""" config_entry = MockConfigEntry( domain=DOMAIN, unique_id=str(CONFIG[DEVICE_ID]), data=CONFIG ) config_entry.add_to_hass(hass) - parameter = MagicMock(spec=EnergyParameter) - parameter.parameter_id = "energy_param" - parameter.name = "Energy" + parameter.parameter_id = "param_id" + parameter.name = "Parameter" + if isinstance(parameter, PercentageParameter): + parameter.unit = PERCENTAGE hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = { PARAMETERS: [parameter], COORDINATOR: mock_coordinator, @@ -171,31 +126,6 @@ async def test_async_setup_entry_wolflink_energy( assert async_add_entities.call_count == 1 entities = async_add_entities.call_args[0][0] assert len(entities) == 1 - assert isinstance(entities[0], WolfLinkEnergy) - - -async def test_async_setup_entry_wolflink_power( - hass: HomeAssistant, mock_coordinator: MagicMock -) -> None: - """Test async_setup_entry for WolfLinkPower.""" - config_entry = MockConfigEntry( - domain=DOMAIN, unique_id=str(CONFIG[DEVICE_ID]), data=CONFIG - ) - config_entry.add_to_hass(hass) - - parameter = MagicMock(spec=PowerParameter) - parameter.parameter_id = "power_param" - parameter.name = "Power" - hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = { - PARAMETERS: [parameter], - COORDINATOR: mock_coordinator, - DEVICE_ID: "mock_device_id", - } - async_add_entities = MagicMock() - - await async_setup_entry(hass, config_entry, async_add_entities) - - assert async_add_entities.call_count == 1 - entities = async_add_entities.call_args[0][0] - assert len(entities) == 1 - assert isinstance(entities[0], WolfLinkPower) + entity = entities[0] + assert isinstance(entity, expected_class) + assert entity.native_unit_of_measurement == expected_unit