Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for the Nettigo Air Monitor service."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime, timedelta
8 import logging
9 
10 from nettigo_air_monitor import NAMSensors
11 
13  DOMAIN as PLATFORM,
14  SensorDeviceClass,
15  SensorEntity,
16  SensorEntityDescription,
17  SensorStateClass,
18 )
19 from homeassistant.const import (
20  CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
21  CONCENTRATION_PARTS_PER_MILLION,
22  PERCENTAGE,
23  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
24  EntityCategory,
25  UnitOfPressure,
26  UnitOfTemperature,
27 )
28 from homeassistant.core import HomeAssistant
29 from homeassistant.helpers import entity_registry as er
30 from homeassistant.helpers.entity_platform import AddEntitiesCallback
31 from homeassistant.helpers.typing import StateType
32 from homeassistant.helpers.update_coordinator import CoordinatorEntity
33 from homeassistant.util.dt import utcnow
34 
35 from . import NAMConfigEntry, NAMDataUpdateCoordinator
36 from .const import (
37  ATTR_BME280_HUMIDITY,
38  ATTR_BME280_PRESSURE,
39  ATTR_BME280_TEMPERATURE,
40  ATTR_BMP180_PRESSURE,
41  ATTR_BMP180_TEMPERATURE,
42  ATTR_BMP280_PRESSURE,
43  ATTR_BMP280_TEMPERATURE,
44  ATTR_DHT22_HUMIDITY,
45  ATTR_DHT22_TEMPERATURE,
46  ATTR_DS18B20_TEMPERATURE,
47  ATTR_HECA_HUMIDITY,
48  ATTR_HECA_TEMPERATURE,
49  ATTR_MHZ14A_CARBON_DIOXIDE,
50  ATTR_PMSX003_CAQI,
51  ATTR_PMSX003_CAQI_LEVEL,
52  ATTR_PMSX003_P0,
53  ATTR_PMSX003_P1,
54  ATTR_PMSX003_P2,
55  ATTR_SDS011_CAQI,
56  ATTR_SDS011_CAQI_LEVEL,
57  ATTR_SDS011_P1,
58  ATTR_SDS011_P2,
59  ATTR_SHT3X_HUMIDITY,
60  ATTR_SHT3X_TEMPERATURE,
61  ATTR_SIGNAL_STRENGTH,
62  ATTR_SPS30_CAQI,
63  ATTR_SPS30_CAQI_LEVEL,
64  ATTR_SPS30_P0,
65  ATTR_SPS30_P1,
66  ATTR_SPS30_P2,
67  ATTR_SPS30_P4,
68  ATTR_UPTIME,
69  DOMAIN,
70  MIGRATION_SENSORS,
71 )
72 
73 PARALLEL_UPDATES = 1
74 
75 _LOGGER = logging.getLogger(__name__)
76 
77 
78 @dataclass(frozen=True, kw_only=True)
80  """NAM sensor entity description."""
81 
82  value: Callable[[NAMSensors], StateType | datetime]
83 
84 
85 SENSORS: tuple[NAMSensorEntityDescription, ...] = (
87  key=ATTR_BME280_HUMIDITY,
88  translation_key="bme280_humidity",
89  suggested_display_precision=1,
90  native_unit_of_measurement=PERCENTAGE,
91  device_class=SensorDeviceClass.HUMIDITY,
92  state_class=SensorStateClass.MEASUREMENT,
93  value=lambda sensors: sensors.bme280_humidity,
94  ),
96  key=ATTR_BME280_PRESSURE,
97  translation_key="bme280_pressure",
98  suggested_display_precision=0,
99  native_unit_of_measurement=UnitOfPressure.HPA,
100  device_class=SensorDeviceClass.PRESSURE,
101  state_class=SensorStateClass.MEASUREMENT,
102  value=lambda sensors: sensors.bme280_pressure,
103  ),
105  key=ATTR_BME280_TEMPERATURE,
106  translation_key="bme280_temperature",
107  suggested_display_precision=1,
108  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
109  device_class=SensorDeviceClass.TEMPERATURE,
110  state_class=SensorStateClass.MEASUREMENT,
111  value=lambda sensors: sensors.bme280_temperature,
112  ),
114  key=ATTR_BMP180_PRESSURE,
115  translation_key="bmp180_pressure",
116  suggested_display_precision=0,
117  native_unit_of_measurement=UnitOfPressure.HPA,
118  device_class=SensorDeviceClass.PRESSURE,
119  state_class=SensorStateClass.MEASUREMENT,
120  value=lambda sensors: sensors.bmp180_pressure,
121  ),
123  key=ATTR_BMP180_TEMPERATURE,
124  translation_key="bmp180_temperature",
125  suggested_display_precision=1,
126  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
127  device_class=SensorDeviceClass.TEMPERATURE,
128  state_class=SensorStateClass.MEASUREMENT,
129  value=lambda sensors: sensors.bmp180_temperature,
130  ),
132  key=ATTR_BMP280_PRESSURE,
133  translation_key="bmp280_pressure",
134  suggested_display_precision=0,
135  native_unit_of_measurement=UnitOfPressure.HPA,
136  device_class=SensorDeviceClass.PRESSURE,
137  state_class=SensorStateClass.MEASUREMENT,
138  value=lambda sensors: sensors.bmp280_pressure,
139  ),
141  key=ATTR_BMP280_TEMPERATURE,
142  translation_key="bmp280_temperature",
143  suggested_display_precision=1,
144  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
145  device_class=SensorDeviceClass.TEMPERATURE,
146  state_class=SensorStateClass.MEASUREMENT,
147  value=lambda sensors: sensors.bmp280_temperature,
148  ),
150  key=ATTR_DS18B20_TEMPERATURE,
151  translation_key="ds18b20_temperature",
152  suggested_display_precision=1,
153  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
154  device_class=SensorDeviceClass.TEMPERATURE,
155  state_class=SensorStateClass.MEASUREMENT,
156  value=lambda sensors: sensors.ds18b20_temperature,
157  ),
159  key=ATTR_HECA_HUMIDITY,
160  translation_key="heca_humidity",
161  suggested_display_precision=1,
162  native_unit_of_measurement=PERCENTAGE,
163  device_class=SensorDeviceClass.HUMIDITY,
164  state_class=SensorStateClass.MEASUREMENT,
165  value=lambda sensors: sensors.heca_humidity,
166  ),
168  key=ATTR_HECA_TEMPERATURE,
169  translation_key="heca_temperature",
170  suggested_display_precision=1,
171  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
172  device_class=SensorDeviceClass.TEMPERATURE,
173  state_class=SensorStateClass.MEASUREMENT,
174  value=lambda sensors: sensors.heca_temperature,
175  ),
177  key=ATTR_MHZ14A_CARBON_DIOXIDE,
178  translation_key="mhz14a_carbon_dioxide",
179  suggested_display_precision=0,
180  native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
181  device_class=SensorDeviceClass.CO2,
182  state_class=SensorStateClass.MEASUREMENT,
183  value=lambda sensors: sensors.mhz14a_carbon_dioxide,
184  ),
186  key=ATTR_PMSX003_CAQI,
187  translation_key="pmsx003_caqi",
188  value=lambda sensors: sensors.pms_caqi,
189  ),
191  key=ATTR_PMSX003_CAQI_LEVEL,
192  translation_key="pmsx003_caqi_level",
193  device_class=SensorDeviceClass.ENUM,
194  options=["very_low", "low", "medium", "high", "very_high"],
195  value=lambda sensors: sensors.pms_caqi_level,
196  ),
198  key=ATTR_PMSX003_P0,
199  translation_key="pmsx003_pm1",
200  suggested_display_precision=0,
201  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
202  device_class=SensorDeviceClass.PM1,
203  state_class=SensorStateClass.MEASUREMENT,
204  value=lambda sensors: sensors.pms_p0,
205  ),
207  key=ATTR_PMSX003_P1,
208  translation_key="pmsx003_pm10",
209  suggested_display_precision=0,
210  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
211  device_class=SensorDeviceClass.PM10,
212  state_class=SensorStateClass.MEASUREMENT,
213  value=lambda sensors: sensors.pms_p1,
214  ),
216  key=ATTR_PMSX003_P2,
217  translation_key="pmsx003_pm25",
218  suggested_display_precision=0,
219  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
220  device_class=SensorDeviceClass.PM25,
221  state_class=SensorStateClass.MEASUREMENT,
222  value=lambda sensors: sensors.pms_p2,
223  ),
225  key=ATTR_SDS011_CAQI,
226  translation_key="sds011_caqi",
227  value=lambda sensors: sensors.sds011_caqi,
228  ),
230  key=ATTR_SDS011_CAQI_LEVEL,
231  translation_key="sds011_caqi_level",
232  device_class=SensorDeviceClass.ENUM,
233  options=["very_low", "low", "medium", "high", "very_high"],
234  value=lambda sensors: sensors.sds011_caqi_level,
235  ),
237  key=ATTR_SDS011_P1,
238  translation_key="sds011_pm10",
239  suggested_display_precision=0,
240  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
241  device_class=SensorDeviceClass.PM10,
242  state_class=SensorStateClass.MEASUREMENT,
243  value=lambda sensors: sensors.sds011_p1,
244  ),
246  key=ATTR_SDS011_P2,
247  translation_key="sds011_pm25",
248  suggested_display_precision=0,
249  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
250  device_class=SensorDeviceClass.PM25,
251  state_class=SensorStateClass.MEASUREMENT,
252  value=lambda sensors: sensors.sds011_p2,
253  ),
255  key=ATTR_SHT3X_HUMIDITY,
256  translation_key="sht3x_humidity",
257  suggested_display_precision=1,
258  native_unit_of_measurement=PERCENTAGE,
259  device_class=SensorDeviceClass.HUMIDITY,
260  state_class=SensorStateClass.MEASUREMENT,
261  value=lambda sensors: sensors.sht3x_humidity,
262  ),
264  key=ATTR_SHT3X_TEMPERATURE,
265  translation_key="sht3x_temperature",
266  suggested_display_precision=1,
267  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
268  device_class=SensorDeviceClass.TEMPERATURE,
269  state_class=SensorStateClass.MEASUREMENT,
270  value=lambda sensors: sensors.sht3x_temperature,
271  ),
273  key=ATTR_SPS30_CAQI,
274  translation_key="sps30_caqi",
275  value=lambda sensors: sensors.sps30_caqi,
276  ),
278  key=ATTR_SPS30_CAQI_LEVEL,
279  translation_key="sps30_caqi_level",
280  device_class=SensorDeviceClass.ENUM,
281  options=["very_low", "low", "medium", "high", "very_high"],
282  value=lambda sensors: sensors.sps30_caqi_level,
283  ),
285  key=ATTR_SPS30_P0,
286  translation_key="sps30_pm1",
287  suggested_display_precision=0,
288  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
289  device_class=SensorDeviceClass.PM1,
290  state_class=SensorStateClass.MEASUREMENT,
291  value=lambda sensors: sensors.sps30_p0,
292  ),
294  key=ATTR_SPS30_P1,
295  translation_key="sps30_pm10",
296  suggested_display_precision=0,
297  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
298  device_class=SensorDeviceClass.PM10,
299  state_class=SensorStateClass.MEASUREMENT,
300  value=lambda sensors: sensors.sps30_p1,
301  ),
303  key=ATTR_SPS30_P2,
304  translation_key="sps30_pm25",
305  suggested_display_precision=0,
306  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
307  device_class=SensorDeviceClass.PM25,
308  state_class=SensorStateClass.MEASUREMENT,
309  value=lambda sensors: sensors.sps30_p2,
310  ),
312  key=ATTR_SPS30_P4,
313  translation_key="sps30_pm4",
314  suggested_display_precision=0,
315  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
316  state_class=SensorStateClass.MEASUREMENT,
317  value=lambda sensors: sensors.sps30_p4,
318  ),
320  key=ATTR_DHT22_HUMIDITY,
321  translation_key="dht22_humidity",
322  suggested_display_precision=1,
323  native_unit_of_measurement=PERCENTAGE,
324  device_class=SensorDeviceClass.HUMIDITY,
325  state_class=SensorStateClass.MEASUREMENT,
326  value=lambda sensors: sensors.dht22_humidity,
327  ),
329  key=ATTR_DHT22_TEMPERATURE,
330  translation_key="dht22_temperature",
331  suggested_display_precision=1,
332  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
333  device_class=SensorDeviceClass.TEMPERATURE,
334  state_class=SensorStateClass.MEASUREMENT,
335  value=lambda sensors: sensors.dht22_temperature,
336  ),
338  key=ATTR_SIGNAL_STRENGTH,
339  suggested_display_precision=0,
340  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
341  device_class=SensorDeviceClass.SIGNAL_STRENGTH,
342  entity_registry_enabled_default=False,
343  state_class=SensorStateClass.MEASUREMENT,
344  entity_category=EntityCategory.DIAGNOSTIC,
345  value=lambda sensors: sensors.signal,
346  ),
348  key=ATTR_UPTIME,
349  translation_key="last_restart",
350  device_class=SensorDeviceClass.TIMESTAMP,
351  entity_registry_enabled_default=False,
352  entity_category=EntityCategory.DIAGNOSTIC,
353  value=lambda sensors: utcnow() - timedelta(seconds=sensors.uptime or 0),
354  ),
355 )
356 
357 
359  hass: HomeAssistant, entry: NAMConfigEntry, async_add_entities: AddEntitiesCallback
360 ) -> None:
361  """Add a Nettigo Air Monitor entities from a config_entry."""
362  coordinator = entry.runtime_data
363 
364  # Due to the change of the attribute name of two sensors, it is necessary to migrate
365  # the unique_ids to the new names.
366  ent_reg = er.async_get(hass)
367  for old_sensor, new_sensor in MIGRATION_SENSORS:
368  old_unique_id = f"{coordinator.unique_id}-{old_sensor}"
369  new_unique_id = f"{coordinator.unique_id}-{new_sensor}"
370  if entity_id := ent_reg.async_get_entity_id(PLATFORM, DOMAIN, old_unique_id):
371  _LOGGER.debug(
372  "Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
373  entity_id,
374  old_unique_id,
375  new_unique_id,
376  )
377  ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
378 
380  NAMSensor(coordinator, description)
381  for description in SENSORS
382  if getattr(coordinator.data, description.key) is not None
383  )
384 
385 
386 class NAMSensor(CoordinatorEntity[NAMDataUpdateCoordinator], SensorEntity):
387  """Define an Nettigo Air Monitor sensor."""
388 
389  _attr_has_entity_name = True
390  entity_description: NAMSensorEntityDescription
391 
392  def __init__(
393  self,
394  coordinator: NAMDataUpdateCoordinator,
395  description: NAMSensorEntityDescription,
396  ) -> None:
397  """Initialize."""
398  super().__init__(coordinator)
399  self._attr_device_info_attr_device_info = coordinator.device_info
400  self._attr_unique_id_attr_unique_id = f"{coordinator.unique_id}-{description.key}"
401  self.entity_descriptionentity_description = description
402 
403  @property
404  def available(self) -> bool:
405  """Return if entity is available."""
406  available = super().available
407 
408  # For a short time after booting, the device does not return values for all
409  # sensors. For this reason, we mark entities for which data is missing as
410  # unavailable.
411  return (
412  available
413  and getattr(self.coordinator.data, self.entity_descriptionentity_description.key) is not None
414  )
415 
416  @property
417  def native_value(self) -> StateType | datetime:
418  """Return the state."""
419  return self.entity_descriptionentity_description.value(self.coordinator.data)
StateType|datetime native_value(self)
Definition: sensor.py:417
None __init__(self, NAMDataUpdateCoordinator coordinator, NAMSensorEntityDescription description)
Definition: sensor.py:396
None async_setup_entry(HomeAssistant hass, NAMConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:360