Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Definition of air-Q sensor platform."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 import logging
8 from typing import Literal
9 
11  SensorDeviceClass,
12  SensorEntity,
13  SensorEntityDescription,
14  SensorStateClass,
15 )
16 from homeassistant.const import (
17  CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
18  CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
19  CONCENTRATION_PARTS_PER_BILLION,
20  CONCENTRATION_PARTS_PER_MILLION,
21  PERCENTAGE,
22  UnitOfPressure,
23  UnitOfSoundPressure,
24  UnitOfTemperature,
25 )
26 from homeassistant.core import HomeAssistant, callback
27 from homeassistant.helpers.entity_platform import AddEntitiesCallback
28 from homeassistant.helpers.update_coordinator import CoordinatorEntity
29 
30 from . import AirQConfigEntry, AirQCoordinator
31 from .const import (
32  ACTIVITY_BECQUEREL_PER_CUBIC_METER,
33  CONCENTRATION_GRAMS_PER_CUBIC_METER,
34 )
35 
36 _LOGGER = logging.getLogger(__name__)
37 
38 
39 @dataclass(frozen=True, kw_only=True)
41  """Describes AirQ sensor entity."""
42 
43  value: Callable[[dict], float | int | None]
44 
45 
46 # Keys must match those in the data dictionary
47 SENSOR_TYPES: list[AirQEntityDescription] = [
49  key="c2h4o",
50  translation_key="acetaldehyde",
51  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
52  state_class=SensorStateClass.MEASUREMENT,
53  value=lambda data: data.get("c2h4o"),
54  ),
56  key="nh3_MR100",
57  translation_key="ammonia",
58  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
59  state_class=SensorStateClass.MEASUREMENT,
60  value=lambda data: data.get("nh3_MR100"),
61  ),
63  key="ash3",
64  translation_key="arsine",
65  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
66  state_class=SensorStateClass.MEASUREMENT,
67  value=lambda data: data.get("ash3"),
68  ),
70  key="br2",
71  translation_key="bromine",
72  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
73  state_class=SensorStateClass.MEASUREMENT,
74  value=lambda data: data.get("br2"),
75  ),
77  key="ch4s",
78  translation_key="methanethiol",
79  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
80  state_class=SensorStateClass.MEASUREMENT,
81  value=lambda data: data.get("ch4s"),
82  ),
84  key="cl2_M20",
85  translation_key="chlorine",
86  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
87  state_class=SensorStateClass.MEASUREMENT,
88  value=lambda data: data.get("cl2_M20"),
89  ),
91  key="clo2",
92  translation_key="chlorine_dioxide",
93  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
94  state_class=SensorStateClass.MEASUREMENT,
95  value=lambda data: data.get("clo2"),
96  ),
98  key="co",
99  translation_key="carbon_monoxide",
100  native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
101  state_class=SensorStateClass.MEASUREMENT,
102  value=lambda data: data.get("co"),
103  ),
105  key="co2",
106  device_class=SensorDeviceClass.CO2,
107  native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
108  state_class=SensorStateClass.MEASUREMENT,
109  value=lambda data: data.get("co2"),
110  ),
112  key="cs2",
113  translation_key="carbon_disulfide",
114  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
115  state_class=SensorStateClass.MEASUREMENT,
116  value=lambda data: data.get("cs2"),
117  ),
119  key="dewpt",
120  translation_key="dew_point",
121  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
122  state_class=SensorStateClass.MEASUREMENT,
123  value=lambda data: data.get("dewpt"),
124  device_class=SensorDeviceClass.TEMPERATURE,
125  ),
127  key="ethanol",
128  translation_key="ethanol",
129  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
130  state_class=SensorStateClass.MEASUREMENT,
131  value=lambda data: data.get("ethanol"),
132  ),
134  key="c2h4",
135  translation_key="ethylene",
136  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
137  state_class=SensorStateClass.MEASUREMENT,
138  value=lambda data: data.get("c2h4"),
139  ),
141  key="ch2o_M10",
142  translation_key="formaldehyde",
143  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
144  state_class=SensorStateClass.MEASUREMENT,
145  value=lambda data: data.get("ch2o_M10"),
146  ),
148  key="f2",
149  translation_key="fluorine",
150  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
151  state_class=SensorStateClass.MEASUREMENT,
152  value=lambda data: data.get("f2"),
153  ),
155  key="h2s",
156  translation_key="hydrogen_sulfide",
157  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
158  state_class=SensorStateClass.MEASUREMENT,
159  value=lambda data: data.get("h2s"),
160  ),
162  key="hcl",
163  translation_key="hydrochloric_acid",
164  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
165  state_class=SensorStateClass.MEASUREMENT,
166  value=lambda data: data.get("hcl"),
167  ),
169  key="hcn",
170  translation_key="hydrogen_cyanide",
171  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
172  state_class=SensorStateClass.MEASUREMENT,
173  value=lambda data: data.get("hcn"),
174  ),
176  key="hf",
177  translation_key="hydrogen_fluoride",
178  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
179  state_class=SensorStateClass.MEASUREMENT,
180  value=lambda data: data.get("hf"),
181  ),
183  key="health",
184  translation_key="health_index",
185  native_unit_of_measurement=PERCENTAGE,
186  state_class=SensorStateClass.MEASUREMENT,
187  value=lambda data: data.get("health", 0.0) / 10.0,
188  ),
190  key="humidity",
191  device_class=SensorDeviceClass.HUMIDITY,
192  native_unit_of_measurement=PERCENTAGE,
193  state_class=SensorStateClass.MEASUREMENT,
194  value=lambda data: data.get("humidity"),
195  ),
197  key="humidity_abs",
198  translation_key="absolute_humidity",
199  native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER,
200  state_class=SensorStateClass.MEASUREMENT,
201  value=lambda data: data.get("humidity_abs"),
202  ),
204  key="h2_M1000",
205  translation_key="hydrogen",
206  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
207  state_class=SensorStateClass.MEASUREMENT,
208  value=lambda data: data.get("h2_M1000"),
209  ),
211  key="h2o2",
212  translation_key="hydrogen_peroxide",
213  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
214  state_class=SensorStateClass.MEASUREMENT,
215  value=lambda data: data.get("h2o2"),
216  ),
218  key="ch4_MIPEX",
219  translation_key="methane",
220  native_unit_of_measurement=PERCENTAGE,
221  state_class=SensorStateClass.MEASUREMENT,
222  value=lambda data: data.get("ch4_MIPEX"),
223  ),
225  key="n2o",
226  device_class=SensorDeviceClass.NITROUS_OXIDE,
227  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
228  state_class=SensorStateClass.MEASUREMENT,
229  value=lambda data: data.get("n2o"),
230  ),
232  key="no_M250",
233  device_class=SensorDeviceClass.NITROGEN_MONOXIDE,
234  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
235  state_class=SensorStateClass.MEASUREMENT,
236  value=lambda data: data.get("no_M250"),
237  ),
239  key="no2",
240  device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
241  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
242  state_class=SensorStateClass.MEASUREMENT,
243  value=lambda data: data.get("no2"),
244  ),
246  key="acid_M100",
247  translation_key="organic_acid",
248  native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
249  state_class=SensorStateClass.MEASUREMENT,
250  value=lambda data: data.get("acid_M100"),
251  ),
253  key="oxygen",
254  translation_key="oxygen",
255  native_unit_of_measurement=PERCENTAGE,
256  state_class=SensorStateClass.MEASUREMENT,
257  value=lambda data: data.get("oxygen"),
258  ),
260  key="o3",
261  device_class=SensorDeviceClass.OZONE,
262  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
263  state_class=SensorStateClass.MEASUREMENT,
264  value=lambda data: data.get("o3"),
265  ),
267  key="performance",
268  translation_key="performance_index",
269  native_unit_of_measurement=PERCENTAGE,
270  state_class=SensorStateClass.MEASUREMENT,
271  value=lambda data: data.get("performance", 0.0) / 10.0,
272  ),
274  key="ph3",
275  translation_key="hydrogen_phosphide",
276  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
277  state_class=SensorStateClass.MEASUREMENT,
278  value=lambda data: data.get("ph3"),
279  ),
281  key="pm1",
282  device_class=SensorDeviceClass.PM1,
283  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
284  state_class=SensorStateClass.MEASUREMENT,
285  value=lambda data: data.get("pm1"),
286  ),
288  key="pm2_5",
289  device_class=SensorDeviceClass.PM25,
290  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
291  state_class=SensorStateClass.MEASUREMENT,
292  value=lambda data: data.get("pm2_5"),
293  ),
295  key="pm10",
296  device_class=SensorDeviceClass.PM10,
297  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
298  state_class=SensorStateClass.MEASUREMENT,
299  value=lambda data: data.get("pm10"),
300  ),
302  key="pressure",
303  device_class=SensorDeviceClass.PRESSURE,
304  native_unit_of_measurement=UnitOfPressure.HPA,
305  state_class=SensorStateClass.MEASUREMENT,
306  value=lambda data: data.get("pressure"),
307  ),
309  key="pressure_rel",
310  translation_key="relative_pressure",
311  native_unit_of_measurement=UnitOfPressure.HPA,
312  state_class=SensorStateClass.MEASUREMENT,
313  value=lambda data: data.get("pressure_rel"),
314  device_class=SensorDeviceClass.PRESSURE,
315  ),
317  key="c3h8_MIPEX",
318  translation_key="propane",
319  native_unit_of_measurement=PERCENTAGE,
320  state_class=SensorStateClass.MEASUREMENT,
321  value=lambda data: data.get("c3h8_MIPEX"),
322  ),
324  key="refigerant",
325  translation_key="refigerant",
326  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
327  state_class=SensorStateClass.MEASUREMENT,
328  value=lambda data: data.get("refigerant"),
329  ),
331  key="sih4",
332  translation_key="silicon_hydride",
333  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
334  state_class=SensorStateClass.MEASUREMENT,
335  value=lambda data: data.get("sih4"),
336  ),
338  key="so2",
339  device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
340  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
341  state_class=SensorStateClass.MEASUREMENT,
342  value=lambda data: data.get("so2"),
343  ),
345  key="sound",
346  translation_key="noise",
347  native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
348  state_class=SensorStateClass.MEASUREMENT,
349  value=lambda data: data.get("sound"),
350  device_class=SensorDeviceClass.SOUND_PRESSURE,
351  ),
353  key="sound_max",
354  translation_key="maximum_noise",
355  native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
356  state_class=SensorStateClass.MEASUREMENT,
357  value=lambda data: data.get("sound_max"),
358  device_class=SensorDeviceClass.SOUND_PRESSURE,
359  ),
361  key="radon",
362  translation_key="radon",
363  native_unit_of_measurement=ACTIVITY_BECQUEREL_PER_CUBIC_METER,
364  state_class=SensorStateClass.MEASUREMENT,
365  value=lambda data: data.get("radon"),
366  ),
368  key="temperature",
369  device_class=SensorDeviceClass.TEMPERATURE,
370  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
371  state_class=SensorStateClass.MEASUREMENT,
372  value=lambda data: data.get("temperature"),
373  ),
375  key="tvoc",
376  device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
377  native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
378  state_class=SensorStateClass.MEASUREMENT,
379  value=lambda data: data.get("tvoc"),
380  ),
382  key="tvoc_ionsc",
383  translation_key="industrial_volatile_organic_compounds",
384  device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
385  native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
386  state_class=SensorStateClass.MEASUREMENT,
387  value=lambda data: data.get("tvoc_ionsc"),
388  ),
390  key="virus",
391  translation_key="virus_index",
392  native_unit_of_measurement=PERCENTAGE,
393  state_class=SensorStateClass.MEASUREMENT,
394  value=lambda data: data.get("virus", 0.0),
395  ),
396 ]
397 
398 
400  hass: HomeAssistant,
401  entry: AirQConfigEntry,
402  async_add_entities: AddEntitiesCallback,
403 ) -> None:
404  """Set up sensor entities based on a config entry."""
405 
406  coordinator = entry.runtime_data
407 
408  entities: list[AirQSensor] = []
409 
410  device_status: dict[str, str] | Literal["OK"] = coordinator.data["Status"]
411 
412  for description in SENSOR_TYPES:
413  if description.key not in coordinator.data:
414  if isinstance(
415  device_status, dict
416  ) and "sensor still in warm up phase" in device_status.get(
417  description.key, "OK"
418  ):
419  # warming up sensors do not contribute keys to coordinator.data
420  # but still must be added
421  _LOGGER.debug("Following sensor is warming up: %s", description.key)
422  else:
423  continue
424  entities.append(AirQSensor(coordinator, description))
425 
426  async_add_entities(entities)
427 
428 
430  """Representation of a Sensor."""
431 
432  _attr_has_entity_name = True
433 
434  def __init__(
435  self,
436  coordinator: AirQCoordinator,
437  description: AirQEntityDescription,
438  ) -> None:
439  """Initialize a single sensor."""
440  super().__init__(coordinator)
441  self.entity_description: AirQEntityDescription = description
442 
443  self._attr_device_info_attr_device_info = coordinator.device_info
444  self._attr_unique_id_attr_unique_id = f"{coordinator.device_id}_{description.key}"
445  self._attr_native_value_attr_native_value = description.value(coordinator.data)
446 
447  @callback
448  def _handle_coordinator_update(self) -> None:
449  """Handle updated data from the coordinator."""
450  self._attr_native_value_attr_native_value = self.entity_description.value(self.coordinator.data)
451  self.async_write_ha_stateasync_write_ha_state()
None __init__(self, AirQCoordinator coordinator, AirQEntityDescription description)
Definition: sensor.py:438
None async_setup_entry(HomeAssistant hass, AirQConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:403