diff --git a/homeassistant/components/adax/sensor.py b/homeassistant/components/adax/sensor.py index f8d54d81558..ca5001b3a10 100644 --- a/homeassistant/components/adax/sensor.py +++ b/homeassistant/components/adax/sensor.py @@ -2,14 +2,16 @@ from __future__ import annotations +from dataclasses import dataclass from typing import cast from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, + SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import UnitOfEnergy +from homeassistant.const import UnitOfEnergy, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -20,44 +22,74 @@ from .const import CONNECTION_TYPE, DOMAIN, LOCAL from .coordinator import AdaxCloudCoordinator +@dataclass(kw_only=True, frozen=True) +class AdaxSensorDescription(SensorEntityDescription): + """Describes Adax sensor entity.""" + + data_key: str + + +SENSORS: tuple[AdaxSensorDescription, ...] = ( + AdaxSensorDescription( + key="temperature", + data_key="temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + ), + AdaxSensorDescription( + key="energy", + data_key="energyWh", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, + suggested_display_precision=3, + ), +) + + async def async_setup_entry( hass: HomeAssistant, entry: AdaxConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: - """Set up the Adax energy sensors with config flow.""" + """Set up the Adax sensors with config flow.""" if entry.data.get(CONNECTION_TYPE) != LOCAL: cloud_coordinator = cast(AdaxCloudCoordinator, entry.runtime_data) # Create individual energy sensors for each device async_add_entities( - AdaxEnergySensor(cloud_coordinator, device_id) - for device_id in cloud_coordinator.data + [ + AdaxSensor(cloud_coordinator, entity_description, device_id) + for device_id in cloud_coordinator.data + for entity_description in SENSORS + ] ) -class AdaxEnergySensor(CoordinatorEntity[AdaxCloudCoordinator], SensorEntity): - """Representation of an Adax energy sensor.""" +class AdaxSensor(CoordinatorEntity[AdaxCloudCoordinator], SensorEntity): + """Representation of an Adax sensor.""" + entity_description: AdaxSensorDescription _attr_has_entity_name = True - _attr_translation_key = "energy" - _attr_device_class = SensorDeviceClass.ENERGY - _attr_native_unit_of_measurement = UnitOfEnergy.WATT_HOUR - _attr_suggested_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR - _attr_state_class = SensorStateClass.TOTAL_INCREASING - _attr_suggested_display_precision = 3 def __init__( self, coordinator: AdaxCloudCoordinator, + entity_description: AdaxSensorDescription, device_id: str, ) -> None: - """Initialize the energy sensor.""" + """Initialize the sensor.""" super().__init__(coordinator) + self.entity_description = entity_description self._device_id = device_id room = coordinator.data[device_id] - self._attr_unique_id = f"{room['homeId']}_{device_id}_energy" + self._attr_unique_id = ( + f"{room['homeId']}_{device_id}_{self.entity_description.key}" + ) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, device_id)}, name=room["name"], @@ -68,10 +100,14 @@ class AdaxEnergySensor(CoordinatorEntity[AdaxCloudCoordinator], SensorEntity): def available(self) -> bool: """Return True if entity is available.""" return ( - super().available and "energyWh" in self.coordinator.data[self._device_id] + super().available + and self.entity_description.data_key + in self.coordinator.data[self._device_id] ) @property - def native_value(self) -> int: + def native_value(self) -> int | float | None: """Return the native value of the sensor.""" - return int(self.coordinator.data[self._device_id]["energyWh"]) + return self.coordinator.data[self._device_id].get( + self.entity_description.data_key + ) diff --git a/tests/components/adax/snapshots/test_sensor.ambr b/tests/components/adax/snapshots/test_sensor.ambr index 7287730727b..ecdf2364301 100644 --- a/tests/components/adax/snapshots/test_sensor.ambr +++ b/tests/components/adax/snapshots/test_sensor.ambr @@ -37,7 +37,7 @@ 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'energy', + 'translation_key': None, 'unique_id': '1_1_energy', 'unit_of_measurement': , }) @@ -58,6 +58,62 @@ 'state': '0.0', }) # --- +# name: test_fallback_to_get_rooms[sensor.room_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.room_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'adax', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1_1_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_fallback_to_get_rooms[sensor.room_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Room 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.room_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '15.0', + }) +# --- # name: test_multiple_devices_create_individual_sensors[sensor.room_1_energy-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -96,7 +152,7 @@ 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'energy', + 'translation_key': None, 'unique_id': '1_1_energy', 'unit_of_measurement': , }) @@ -117,6 +173,62 @@ 'state': '1.5', }) # --- +# name: test_multiple_devices_create_individual_sensors[sensor.room_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.room_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'adax', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1_1_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_multiple_devices_create_individual_sensors[sensor.room_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Room 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.room_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '15.0', + }) +# --- # name: test_multiple_devices_create_individual_sensors[sensor.room_2_energy-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -155,7 +267,7 @@ 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'energy', + 'translation_key': None, 'unique_id': '1_2_energy', 'unit_of_measurement': , }) @@ -176,6 +288,62 @@ 'state': '2.5', }) # --- +# name: test_multiple_devices_create_individual_sensors[sensor.room_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.room_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'adax', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1_2_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_multiple_devices_create_individual_sensors[sensor.room_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Room 2 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.room_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '18.0', + }) +# --- # name: test_sensor_cloud[sensor.room_1_energy-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -214,7 +382,7 @@ 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'energy', + 'translation_key': None, 'unique_id': '1_1_energy', 'unit_of_measurement': , }) @@ -235,3 +403,59 @@ 'state': '1.5', }) # --- +# name: test_sensor_cloud[sensor.room_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.room_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'adax', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1_1_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_cloud[sensor.room_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Room 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.room_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '15', + }) +# --- diff --git a/tests/components/adax/test_sensor.py b/tests/components/adax/test_sensor.py index 0274ebe2b15..24ec38a51f1 100644 --- a/tests/components/adax/test_sensor.py +++ b/tests/components/adax/test_sensor.py @@ -60,7 +60,7 @@ async def test_multiple_devices_create_individual_sensors( "id": "1", "homeId": "1", "name": "Room 1", - "temperature": 15, + "temperature": 15.0, "targetTemperature": 20, "heatingEnabled": True, "energyWh": 1500, @@ -69,7 +69,7 @@ async def test_multiple_devices_create_individual_sensors( "id": "2", "homeId": "1", "name": "Room 2", - "temperature": 18, + "temperature": 18.0, "targetTemperature": 22, "heatingEnabled": True, "energyWh": 2500, @@ -102,7 +102,7 @@ async def test_fallback_to_get_rooms( "id": "1", "homeId": "1", "name": "Room 1", - "temperature": 15, + "temperature": 15.0, "targetTemperature": 20, "heatingEnabled": True, "energyWh": 0, # No energy data from get_rooms