Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for the Airly sensor service."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import Any
8 
10  SensorDeviceClass,
11  SensorEntity,
12  SensorEntityDescription,
13  SensorStateClass,
14 )
15 from homeassistant.const import (
16  CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
17  CONF_NAME,
18  PERCENTAGE,
19  UnitOfPressure,
20  UnitOfTemperature,
21 )
22 from homeassistant.core import HomeAssistant, callback
23 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.helpers.update_coordinator import CoordinatorEntity
26 
27 from . import AirlyConfigEntry, AirlyDataUpdateCoordinator
28 from .const import (
29  ATTR_ADVICE,
30  ATTR_API_ADVICE,
31  ATTR_API_CAQI,
32  ATTR_API_CAQI_DESCRIPTION,
33  ATTR_API_CAQI_LEVEL,
34  ATTR_API_CO,
35  ATTR_API_HUMIDITY,
36  ATTR_API_NO2,
37  ATTR_API_O3,
38  ATTR_API_PM1,
39  ATTR_API_PM10,
40  ATTR_API_PM25,
41  ATTR_API_PRESSURE,
42  ATTR_API_SO2,
43  ATTR_API_TEMPERATURE,
44  ATTR_DESCRIPTION,
45  ATTR_LEVEL,
46  ATTR_LIMIT,
47  ATTR_PERCENT,
48  ATTRIBUTION,
49  DOMAIN,
50  MANUFACTURER,
51  SUFFIX_LIMIT,
52  SUFFIX_PERCENT,
53  URL,
54 )
55 
56 PARALLEL_UPDATES = 1
57 
58 
59 @dataclass(frozen=True)
61  """Class describing Airly sensor entities."""
62 
63  attrs: Callable[[dict[str, Any]], dict[str, Any]] = lambda data: {}
64 
65 
66 SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
68  key=ATTR_API_CAQI,
69  translation_key="caqi",
70  native_unit_of_measurement="CAQI",
71  suggested_display_precision=0,
72  attrs=lambda data: {
73  ATTR_LEVEL: data[ATTR_API_CAQI_LEVEL],
74  ATTR_ADVICE: data[ATTR_API_ADVICE],
75  ATTR_DESCRIPTION: data[ATTR_API_CAQI_DESCRIPTION],
76  },
77  ),
79  key=ATTR_API_PM1,
80  device_class=SensorDeviceClass.PM1,
81  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
82  state_class=SensorStateClass.MEASUREMENT,
83  suggested_display_precision=0,
84  ),
86  key=ATTR_API_PM25,
87  device_class=SensorDeviceClass.PM25,
88  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
89  state_class=SensorStateClass.MEASUREMENT,
90  suggested_display_precision=0,
91  attrs=lambda data: {
92  ATTR_LIMIT: data[f"{ATTR_API_PM25}_{SUFFIX_LIMIT}"],
93  ATTR_PERCENT: round(data[f"{ATTR_API_PM25}_{SUFFIX_PERCENT}"]),
94  },
95  ),
97  key=ATTR_API_PM10,
98  device_class=SensorDeviceClass.PM10,
99  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
100  state_class=SensorStateClass.MEASUREMENT,
101  suggested_display_precision=0,
102  attrs=lambda data: {
103  ATTR_LIMIT: data[f"{ATTR_API_PM10}_{SUFFIX_LIMIT}"],
104  ATTR_PERCENT: round(data[f"{ATTR_API_PM10}_{SUFFIX_PERCENT}"]),
105  },
106  ),
108  key=ATTR_API_HUMIDITY,
109  device_class=SensorDeviceClass.HUMIDITY,
110  native_unit_of_measurement=PERCENTAGE,
111  state_class=SensorStateClass.MEASUREMENT,
112  suggested_display_precision=1,
113  ),
115  key=ATTR_API_PRESSURE,
116  device_class=SensorDeviceClass.PRESSURE,
117  native_unit_of_measurement=UnitOfPressure.HPA,
118  state_class=SensorStateClass.MEASUREMENT,
119  suggested_display_precision=0,
120  ),
122  key=ATTR_API_TEMPERATURE,
123  device_class=SensorDeviceClass.TEMPERATURE,
124  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
125  state_class=SensorStateClass.MEASUREMENT,
126  suggested_display_precision=1,
127  ),
129  key=ATTR_API_CO,
130  translation_key="co",
131  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
132  state_class=SensorStateClass.MEASUREMENT,
133  suggested_display_precision=0,
134  attrs=lambda data: {
135  ATTR_LIMIT: data[f"{ATTR_API_CO}_{SUFFIX_LIMIT}"],
136  ATTR_PERCENT: round(data[f"{ATTR_API_CO}_{SUFFIX_PERCENT}"]),
137  },
138  ),
140  key=ATTR_API_NO2,
141  device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
142  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
143  state_class=SensorStateClass.MEASUREMENT,
144  suggested_display_precision=0,
145  attrs=lambda data: {
146  ATTR_LIMIT: data[f"{ATTR_API_NO2}_{SUFFIX_LIMIT}"],
147  ATTR_PERCENT: round(data[f"{ATTR_API_NO2}_{SUFFIX_PERCENT}"]),
148  },
149  ),
151  key=ATTR_API_SO2,
152  device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
153  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
154  state_class=SensorStateClass.MEASUREMENT,
155  suggested_display_precision=0,
156  attrs=lambda data: {
157  ATTR_LIMIT: data[f"{ATTR_API_SO2}_{SUFFIX_LIMIT}"],
158  ATTR_PERCENT: round(data[f"{ATTR_API_SO2}_{SUFFIX_PERCENT}"]),
159  },
160  ),
162  key=ATTR_API_O3,
163  device_class=SensorDeviceClass.OZONE,
164  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
165  state_class=SensorStateClass.MEASUREMENT,
166  suggested_display_precision=0,
167  attrs=lambda data: {
168  ATTR_LIMIT: data[f"{ATTR_API_O3}_{SUFFIX_LIMIT}"],
169  ATTR_PERCENT: round(data[f"{ATTR_API_O3}_{SUFFIX_PERCENT}"]),
170  },
171  ),
172 )
173 
174 
176  hass: HomeAssistant,
177  entry: AirlyConfigEntry,
178  async_add_entities: AddEntitiesCallback,
179 ) -> None:
180  """Set up Airly sensor entities based on a config entry."""
181  name = entry.data[CONF_NAME]
182 
183  coordinator = entry.runtime_data
184 
186  (
187  AirlySensor(coordinator, name, description)
188  for description in SENSOR_TYPES
189  # When we use the nearest method, we are not sure which sensors are available
190  if coordinator.data.get(description.key)
191  ),
192  False,
193  )
194 
195 
196 class AirlySensor(CoordinatorEntity[AirlyDataUpdateCoordinator], SensorEntity):
197  """Define an Airly sensor."""
198 
199  _attr_attribution = ATTRIBUTION
200  _attr_has_entity_name = True
201  entity_description: AirlySensorEntityDescription
202 
203  def __init__(
204  self,
205  coordinator: AirlyDataUpdateCoordinator,
206  name: str,
207  description: AirlySensorEntityDescription,
208  ) -> None:
209  """Initialize."""
210  super().__init__(coordinator)
211  self._attr_device_info_attr_device_info = DeviceInfo(
212  entry_type=DeviceEntryType.SERVICE,
213  identifiers={(DOMAIN, f"{coordinator.latitude}-{coordinator.longitude}")},
214  manufacturer=MANUFACTURER,
215  name=name,
216  configuration_url=URL.format(
217  latitude=coordinator.latitude, longitude=coordinator.longitude
218  ),
219  )
220  self._attr_unique_id_attr_unique_id = (
221  f"{coordinator.latitude}-{coordinator.longitude}-{description.key}".lower()
222  )
223  self._attr_native_value_attr_native_value = coordinator.data[description.key]
224  self._attr_extra_state_attributes_attr_extra_state_attributes = description.attrs(coordinator.data)
225  self.entity_descriptionentity_description = description
226 
227  @callback
228  def _handle_coordinator_update(self) -> None:
229  """Handle updated data from the coordinator."""
230  self._attr_native_value_attr_native_value = self.coordinator.data[self.entity_descriptionentity_description.key]
231  self._attr_extra_state_attributes_attr_extra_state_attributes = self.entity_descriptionentity_description.attrs(
232  self.coordinator.data
233  )
234  self.async_write_ha_stateasync_write_ha_state()
None __init__(self, AirlyDataUpdateCoordinator coordinator, str name, AirlySensorEntityDescription description)
Definition: sensor.py:208
None async_setup_entry(HomeAssistant hass, AirlyConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:179