From c0e84a7b3227368549f5fbfb64cfa30358c296dc Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Tue, 27 Jul 2021 12:08:31 +0200 Subject: [PATCH] Use SensorEntityDescription in Airly integration (#53540) --- homeassistant/components/airly/const.py | 109 +++++++++++------------ homeassistant/components/airly/model.py | 17 ++-- homeassistant/components/airly/sensor.py | 70 ++++++--------- 3 files changed, 88 insertions(+), 108 deletions(-) diff --git a/homeassistant/components/airly/const.py b/homeassistant/components/airly/const.py index d79a33a66ab..157f28c33f7 100644 --- a/homeassistant/components/airly/const.py +++ b/homeassistant/components/airly/const.py @@ -3,10 +3,8 @@ from __future__ import annotations from typing import Final -from homeassistant.components.sensor import ATTR_STATE_CLASS, STATE_CLASS_MEASUREMENT +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT from homeassistant.const import ( - ATTR_DEVICE_CLASS, - ATTR_ICON, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, @@ -16,7 +14,7 @@ from homeassistant.const import ( TEMP_CELSIUS, ) -from .model import SensorDescription +from .model import AirlySensorEntityDescription ATTR_API_ADVICE: Final = "ADVICE" ATTR_API_CAQI: Final = "CAQI" @@ -31,12 +29,9 @@ ATTR_API_TEMPERATURE: Final = "TEMPERATURE" ATTR_ADVICE: Final = "advice" ATTR_DESCRIPTION: Final = "description" -ATTR_LABEL: Final = "label" ATTR_LEVEL: Final = "level" ATTR_LIMIT: Final = "limit" ATTR_PERCENT: Final = "percent" -ATTR_UNIT: Final = "unit" -ATTR_VALUE: Final = "value" SUFFIX_PERCENT: Final = "PERCENT" SUFFIX_LIMIT: Final = "LIMIT" @@ -51,52 +46,54 @@ MAX_UPDATE_INTERVAL: Final = 90 MIN_UPDATE_INTERVAL: Final = 5 NO_AIRLY_SENSORS: Final = "There are no Airly sensors in this area yet." -SENSOR_TYPES: dict[str, SensorDescription] = { - ATTR_API_CAQI: { - ATTR_LABEL: ATTR_API_CAQI, - ATTR_UNIT: "CAQI", - ATTR_VALUE: round, - }, - ATTR_API_PM1: { - ATTR_ICON: "mdi:blur", - ATTR_LABEL: ATTR_API_PM1, - ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - ATTR_VALUE: round, - }, - ATTR_API_PM25: { - ATTR_ICON: "mdi:blur", - ATTR_LABEL: "PM2.5", - ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - ATTR_VALUE: round, - }, - ATTR_API_PM10: { - ATTR_ICON: "mdi:blur", - ATTR_LABEL: ATTR_API_PM10, - ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - ATTR_VALUE: round, - }, - ATTR_API_HUMIDITY: { - ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY, - ATTR_LABEL: ATTR_API_HUMIDITY.capitalize(), - ATTR_UNIT: PERCENTAGE, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - ATTR_VALUE: lambda value: round(value, 1), - }, - ATTR_API_PRESSURE: { - ATTR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE, - ATTR_LABEL: ATTR_API_PRESSURE.capitalize(), - ATTR_UNIT: PRESSURE_HPA, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - ATTR_VALUE: round, - }, - ATTR_API_TEMPERATURE: { - ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - ATTR_LABEL: ATTR_API_TEMPERATURE.capitalize(), - ATTR_UNIT: TEMP_CELSIUS, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - ATTR_VALUE: lambda value: round(value, 1), - }, -} +SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = ( + AirlySensorEntityDescription( + key=ATTR_API_CAQI, + name=ATTR_API_CAQI, + unit_of_measurement="CAQI", + ), + AirlySensorEntityDescription( + key=ATTR_API_PM1, + icon="mdi:blur", + name=ATTR_API_PM1, + unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=STATE_CLASS_MEASUREMENT, + ), + AirlySensorEntityDescription( + key=ATTR_API_PM25, + icon="mdi:blur", + name="PM2.5", + unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=STATE_CLASS_MEASUREMENT, + ), + AirlySensorEntityDescription( + key=ATTR_API_PM10, + icon="mdi:blur", + name=ATTR_API_PM10, + unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=STATE_CLASS_MEASUREMENT, + ), + AirlySensorEntityDescription( + key=ATTR_API_HUMIDITY, + device_class=DEVICE_CLASS_HUMIDITY, + name=ATTR_API_HUMIDITY.capitalize(), + unit_of_measurement=PERCENTAGE, + state_class=STATE_CLASS_MEASUREMENT, + value=lambda value: round(value, 1), + ), + AirlySensorEntityDescription( + key=ATTR_API_PRESSURE, + device_class=DEVICE_CLASS_PRESSURE, + name=ATTR_API_PRESSURE.capitalize(), + unit_of_measurement=PRESSURE_HPA, + state_class=STATE_CLASS_MEASUREMENT, + ), + AirlySensorEntityDescription( + key=ATTR_API_TEMPERATURE, + device_class=DEVICE_CLASS_TEMPERATURE, + name=ATTR_API_TEMPERATURE.capitalize(), + unit_of_measurement=TEMP_CELSIUS, + state_class=STATE_CLASS_MEASUREMENT, + value=lambda value: round(value, 1), + ), +) diff --git a/homeassistant/components/airly/model.py b/homeassistant/components/airly/model.py index fe8ad6c929b..38b433de34c 100644 --- a/homeassistant/components/airly/model.py +++ b/homeassistant/components/airly/model.py @@ -1,15 +1,14 @@ """Type definitions for Airly integration.""" from __future__ import annotations -from typing import Callable, TypedDict +from dataclasses import dataclass +from typing import Callable + +from homeassistant.components.sensor import SensorEntityDescription -class SensorDescription(TypedDict, total=False): - """Sensor description class.""" +@dataclass +class AirlySensorEntityDescription(SensorEntityDescription): + """Class describing Airly sensor entities.""" - device_class: str | None - icon: str | None - label: str - unit: str - state_class: str | None - value: Callable + value: Callable = round diff --git a/homeassistant/components/airly/sensor.py b/homeassistant/components/airly/sensor.py index 3f9048dd03e..2c811b00aa6 100644 --- a/homeassistant/components/airly/sensor.py +++ b/homeassistant/components/airly/sensor.py @@ -3,16 +3,10 @@ from __future__ import annotations from typing import Any, cast -from homeassistant.components.sensor import ATTR_STATE_CLASS, SensorEntity +from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_ATTRIBUTION, - ATTR_DEVICE_CLASS, - ATTR_ICON, - CONF_NAME, -) +from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -27,12 +21,9 @@ from .const import ( ATTR_API_PM10, ATTR_API_PM25, ATTR_DESCRIPTION, - ATTR_LABEL, ATTR_LEVEL, ATTR_LIMIT, ATTR_PERCENT, - ATTR_UNIT, - ATTR_VALUE, ATTRIBUTION, DEFAULT_NAME, DOMAIN, @@ -41,6 +32,7 @@ from .const import ( SUFFIX_LIMIT, SUFFIX_PERCENT, ) +from .model import AirlySensorEntityDescription PARALLEL_UPDATES = 1 @@ -54,10 +46,10 @@ async def async_setup_entry( coordinator = hass.data[DOMAIN][entry.entry_id] sensors = [] - for sensor in SENSOR_TYPES: + for description in SENSOR_TYPES: # When we use the nearest method, we are not sure which sensors are available - if coordinator.data.get(sensor): - sensors.append(AirlySensor(coordinator, name, sensor)) + if coordinator.data.get(description.key): + sensors.append(AirlySensor(coordinator, name, description)) async_add_entities(sensors, False) @@ -66,47 +58,54 @@ class AirlySensor(CoordinatorEntity, SensorEntity): """Define an Airly sensor.""" coordinator: AirlyDataUpdateCoordinator + entity_description: AirlySensorEntityDescription def __init__( - self, coordinator: AirlyDataUpdateCoordinator, name: str, kind: str + self, + coordinator: AirlyDataUpdateCoordinator, + name: str, + description: AirlySensorEntityDescription, ) -> None: """Initialize.""" super().__init__(coordinator) - self._description = description = SENSOR_TYPES[kind] - self._attr_device_class = description.get(ATTR_DEVICE_CLASS) - self._attr_icon = description.get(ATTR_ICON) - self._attr_name = f"{name} {description[ATTR_LABEL]}" - self._attr_state_class = description.get(ATTR_STATE_CLASS) + self._attr_device_info = { + "identifiers": { + (DOMAIN, f"{coordinator.latitude}-{coordinator.longitude}") + }, + "name": DEFAULT_NAME, + "manufacturer": MANUFACTURER, + "entry_type": "service", + } + self._attr_name = f"{name} {description.name}" self._attr_unique_id = ( - f"{coordinator.latitude}-{coordinator.longitude}-{kind.lower()}" + f"{coordinator.latitude}-{coordinator.longitude}-{description.key}".lower() ) - self._attr_unit_of_measurement = description.get(ATTR_UNIT) self._attrs: dict[str, Any] = {ATTR_ATTRIBUTION: ATTRIBUTION} - self.kind = kind + self.entity_description = description @property def state(self) -> StateType: """Return the state.""" - state = self.coordinator.data[self.kind] - return cast(StateType, self._description[ATTR_VALUE](state)) + state = self.coordinator.data[self.entity_description.key] + return cast(StateType, self.entity_description.value(state)) @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes.""" - if self.kind == ATTR_API_CAQI: + if self.entity_description.key == ATTR_API_CAQI: self._attrs[ATTR_LEVEL] = self.coordinator.data[ATTR_API_CAQI_LEVEL] self._attrs[ATTR_ADVICE] = self.coordinator.data[ATTR_API_ADVICE] self._attrs[ATTR_DESCRIPTION] = self.coordinator.data[ ATTR_API_CAQI_DESCRIPTION ] - if self.kind == ATTR_API_PM25: + if self.entity_description.key == ATTR_API_PM25: self._attrs[ATTR_LIMIT] = self.coordinator.data[ f"{ATTR_API_PM25}_{SUFFIX_LIMIT}" ] self._attrs[ATTR_PERCENT] = round( self.coordinator.data[f"{ATTR_API_PM25}_{SUFFIX_PERCENT}"] ) - if self.kind == ATTR_API_PM10: + if self.entity_description.key == ATTR_API_PM10: self._attrs[ATTR_LIMIT] = self.coordinator.data[ f"{ATTR_API_PM10}_{SUFFIX_LIMIT}" ] @@ -114,18 +113,3 @@ class AirlySensor(CoordinatorEntity, SensorEntity): self.coordinator.data[f"{ATTR_API_PM10}_{SUFFIX_PERCENT}"] ) return self._attrs - - @property - def device_info(self) -> DeviceInfo: - """Return the device info.""" - return { - "identifiers": { - ( - DOMAIN, - f"{self.coordinator.latitude}-{self.coordinator.longitude}", - ) - }, - "name": DEFAULT_NAME, - "manufacturer": MANUFACTURER, - "entry_type": "service", - }