Add name_fn and test_sensor adoptions

This commit is contained in:
EnjoyingM
2025-02-26 23:25:45 +00:00
parent 4cf362d6d5
commit f223b8a190
3 changed files with 148 additions and 169 deletions

View File

@@ -39,6 +39,70 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import COORDINATOR, DEVICE_ID, DOMAIN, MANUFACTURER, PARAMETERS, STATES from .const import COORDINATOR, DEVICE_ID, DOMAIN, MANUFACTURER, PARAMETERS, STATES
@dataclass(kw_only=True, frozen=True)
class WolflinkSensorEntityDescription(SensorEntityDescription):
"""Describes Wolflink sensor entity."""
name_fn: Callable[[Parameter], str]
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),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=UnitOfPressure.BAR,
supported_fn=lambda param: isinstance(param, Pressure),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="energy",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
supported_fn=lambda param: isinstance(param, EnergyParameter),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
supported_fn=lambda param: isinstance(param, PowerParameter),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="percentage",
native_unit_of_measurement=PERCENTAGE,
supported_fn=lambda param: isinstance(param, PercentageParameter),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="list_item",
translation_key="state",
supported_fn=lambda param: isinstance(param, ListItemParameter),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="hours",
icon="mdi:clock",
native_unit_of_measurement=UnitOfTime.HOURS,
supported_fn=lambda param: isinstance(param, HoursParameter),
name_fn=lambda param: param.name,
),
WolflinkSensorEntityDescription(
key="default",
supported_fn=lambda param: isinstance(param, SimpleParameter),
name_fn=lambda param: param.name,
),
]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ConfigEntry,
@@ -50,93 +114,15 @@ async def async_setup_entry(
device_id = hass.data[DOMAIN][config_entry.entry_id][DEVICE_ID] device_id = hass.data[DOMAIN][config_entry.entry_id][DEVICE_ID]
entities: list[WolfLinkSensor] = [ entities: list[WolfLinkSensor] = [
WolfLinkSensor( WolfLinkSensor(coordinator, parameter, device_id, description)
coordinator, parameter, device_id, get_entity_description(parameter)
)
for parameter in parameters for parameter in parameters
for description in SENSOR_DESCRIPTIONS
if description.supported_fn(parameter)
] ]
async_add_entities(entities, True) async_add_entities(entities, True)
@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."""
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): class WolfLinkSensor(CoordinatorEntity, SensorEntity):
"""Base class for all Wolf entities.""" """Base class for all Wolf entities."""
@@ -153,7 +139,7 @@ class WolfLinkSensor(CoordinatorEntity, SensorEntity):
super().__init__(coordinator) super().__init__(coordinator)
self.entity_description = description self.entity_description = description
self.wolf_object = wolf_object self.wolf_object = wolf_object
self._attr_name = str(description.name) self._attr_name = description.name_fn(wolf_object)
self._attr_unique_id = f"{device_id}:{wolf_object.parameter_id}" self._attr_unique_id = f"{device_id}:{wolf_object.parameter_id}"
self._state = None self._state = None
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(

View File

@@ -0,0 +1,25 @@
# serializer version: 1
# name: test_sensor[wolf_parameter0]
None
# ---
# name: test_sensor[wolf_parameter1]
None
# ---
# name: test_sensor[wolf_parameter2]
None
# ---
# name: test_sensor[wolf_parameter3]
None
# ---
# name: test_sensor[wolf_parameter4]
None
# ---
# name: test_sensor[wolf_parameter5]
None
# ---
# name: test_sensor[wolf_parameter6]
None
# ---
# name: test_sensor[wolf_parameter7]
None
# ---

View File

@@ -3,8 +3,8 @@
from unittest.mock import MagicMock, Mock from unittest.mock import MagicMock, Mock
import pytest import pytest
from syrupy import SnapshotAssertion
from wolf_comm import ( from wolf_comm import (
PERCENTAGE,
EnergyParameter, EnergyParameter,
HoursParameter, HoursParameter,
ListItemParameter, ListItemParameter,
@@ -20,15 +20,11 @@ from homeassistant.components.wolflink.const import (
COORDINATOR, COORDINATOR,
DEVICE_ID, DEVICE_ID,
DOMAIN, DOMAIN,
MANUFACTURER,
PARAMETERS, PARAMETERS,
) )
from homeassistant.components.wolflink.sensor import ( from homeassistant.components.wolflink.sensor import WolfLinkSensor, async_setup_entry
WolfLinkSensor,
async_setup_entry,
get_entity_description,
)
from homeassistant.const import ( from homeassistant.const import (
PERCENTAGE,
UnitOfEnergy, UnitOfEnergy,
UnitOfPower, UnitOfPower,
UnitOfPressure, UnitOfPressure,
@@ -44,22 +40,13 @@ from tests.common import MockConfigEntry
@pytest.fixture @pytest.fixture
def mock_coordinator(hass: HomeAssistant) -> MagicMock: def mock_device_id() -> str:
"""Mock the Wolf SmartSet Service Coordinator."""
coordinator = MagicMock()
coordinator.data = {}
hass.data[DOMAIN] = {CONFIG[DEVICE_ID]: {COORDINATOR: coordinator}}
return coordinator
@pytest.fixture
def mock_device_id():
"""Fixture for a mock device ID.""" """Fixture for a mock device ID."""
return "1234" return "1234"
@pytest.fixture @pytest.fixture
def mock_parameter(): def mock_parameter() -> Parameter:
"""Fixture for a mock parameter.""" """Fixture for a mock parameter."""
return Mock(spec=Parameter) return Mock(spec=Parameter)
@@ -78,22 +65,24 @@ async def mock_config_entry(
assert device.identifiers == {(DOMAIN, CONFIG[DEVICE_ID])} assert device.identifiers == {(DOMAIN, CONFIG[DEVICE_ID])}
def test_wolflink_sensor_native_value(mock_coordinator: MagicMock) -> None: def test_wolflink_sensor_native_value() -> None:
"""Test WolflinkSensor native value.""" """Test WolflinkSensor native value."""
coordinator = MagicMock()
parameter = MagicMock() parameter = MagicMock()
parameter.parameter_id = "outside_temp" parameter.parameter_id = "outside_temp"
sensor = WolfLinkSensor(mock_coordinator, parameter, "mock_device_id", MagicMock()) sensor = WolfLinkSensor(coordinator, parameter, "mock_device_id", MagicMock())
mock_coordinator.data = {"outside_temp": [None, 20]} coordinator.data = {"outside_temp": [None, 20]}
assert sensor.native_value == 20 assert sensor.native_value == 20
def test_wolflink_sensor_extra_state_attributes(mock_coordinator: MagicMock) -> None: def test_wolflink_sensor_extra_state_attributes() -> None:
"""Test WolflinkSensor extra state attributes.""" """Test WolflinkSensor extra state attributes."""
coordinator = MagicMock()
parameter = MagicMock() parameter = MagicMock()
parameter.parameter_id = "outside_temp" parameter.parameter_id = "outside_temp"
parameter.value_id = "value_id" parameter.value_id = "value_id"
parameter.parent = "parent" parameter.parent = "parent"
sensor = WolfLinkSensor(mock_coordinator, parameter, "mock_device_id", MagicMock()) sensor = WolfLinkSensor(coordinator, parameter, "mock_device_id", MagicMock())
attributes = sensor.extra_state_attributes attributes = sensor.extra_state_attributes
assert attributes["parameter_id"] == "outside_temp" assert attributes["parameter_id"] == "outside_temp"
assert attributes["value_id"] == "value_id" assert attributes["value_id"] == "value_id"
@@ -113,12 +102,13 @@ def test_wolflink_sensor_extra_state_attributes(mock_coordinator: MagicMock) ->
) )
async def test_async_setup_entry( async def test_async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
mock_coordinator: MagicMock,
parameter: Parameter, parameter: Parameter,
wolf_parameter: Parameter,
expected_class: type, expected_class: type,
expected_unit: str, expected_unit: str,
) -> None: ) -> None:
"""Test async_setup_entry for various parameter types.""" """Test async_setup_entry for various parameter types."""
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
domain=DOMAIN, unique_id=str(CONFIG[DEVICE_ID]), data=CONFIG domain=DOMAIN, unique_id=str(CONFIG[DEVICE_ID]), data=CONFIG
) )
@@ -130,8 +120,8 @@ async def test_async_setup_entry(
parameter.unit = PERCENTAGE parameter.unit = PERCENTAGE
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = { hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = {
PARAMETERS: [parameter], PARAMETERS: [parameter],
COORDINATOR: mock_coordinator,
DEVICE_ID: "1234", DEVICE_ID: "1234",
COORDINATOR: MagicMock(), # Ensure COORDINATOR is set up
} }
async_add_entities = MagicMock() async_add_entities = MagicMock()
@@ -145,70 +135,48 @@ async def test_async_setup_entry(
assert entity.native_unit_of_measurement == expected_unit assert entity.native_unit_of_measurement == expected_unit
def test_get_entity_description() -> None: @pytest.fixture(
"""Test the get_entity_description function.""" params=[
parameter = Mock(spec=Temperature) EnergyParameter(6002800000, "Energy Parameter", "Heating", 6005200000),
description = get_entity_description(parameter) ListItemParameter(
assert description.device_class == "temperature" 8002800000,
assert description.native_unit_of_measurement == "°C" "List Item Parameter",
"Heating",
parameter = Mock(spec=Pressure) (["Pump", "on"], ["Heating", "on"]),
description = get_entity_description(parameter) 8005200000,
assert description.device_class == "pressure" ),
assert description.native_unit_of_measurement == "bar" PowerParameter(5002800000, "Power Parameter", "Heating", 5005200000),
Pressure(4002800000, "Pressure Parameter", "Heating", 4005200000),
parameter = Mock(spec=EnergyParameter) Temperature(3002800000, "Temperature Parameter", "Solar", 3005200000),
description = get_entity_description(parameter) PercentageParameter(2002800000, "Percentage Parameter", "Solar", 2005200000),
assert description.device_class == "energy" HoursParameter(7002800000, "Hours Parameter", "Heating", 7005200000),
assert description.native_unit_of_measurement == "kWh" SimpleParameter(1002800000, "Simple Parameter", "DHW", 1005200000),
]
parameter = Mock(spec=PowerParameter) )
description = get_entity_description(parameter) def wolf_parameter(request: pytest.FixtureRequest) -> Parameter:
assert description.device_class == "power" """Fixture for different WolfLink parameter types."""
assert description.native_unit_of_measurement == "kW" return request.param
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, mock_parameter) -> None: async def test_sensor(
"""Test the WolfLinkSensor class.""" hass: HomeAssistant,
description = get_entity_description(mock_parameter) snapshot: SnapshotAssertion,
sensor = WolfLinkSensor( wolf_parameter: Parameter,
mock_coordinator, mock_parameter, mock_device_id, description ) -> None:
"""Test the sensor state for various parameter types."""
config_entry = MockConfigEntry(
domain=DOMAIN, unique_id=str(CONFIG[DEVICE_ID]), data=CONFIG
) )
config_entry.add_to_hass(hass)
assert sensor.entity_description == description hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = {
assert sensor.wolf_object == mock_parameter PARAMETERS: [wolf_parameter],
assert sensor._attr_name == str(description.name) DEVICE_ID: "1234",
assert sensor._attr_unique_id == f"{mock_device_id}:{mock_parameter.parameter_id}" COORDINATOR: MagicMock(), # Ensure COORDINATOR is set up
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,
} }
async_add_entities = MagicMock()
await async_setup_entry(hass, config_entry, async_add_entities)
state = hass.states.get(f"{wolf_parameter.parameter_id}")
assert state == snapshot