Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for AirGradient sensors."""
2 
3 from collections.abc import Callable
4 from dataclasses import dataclass
5 
6 from airgradient import Config
7 from airgradient.models import (
8  ConfigurationControl,
9  LedBarMode,
10  Measures,
11  TemperatureUnit,
12 )
13 
15  SensorDeviceClass,
16  SensorEntity,
17  SensorEntityDescription,
18  SensorStateClass,
19 )
20 from homeassistant.const import (
21  CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
22  CONCENTRATION_PARTS_PER_MILLION,
23  PERCENTAGE,
24  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
25  EntityCategory,
26  UnitOfTemperature,
27  UnitOfTime,
28 )
29 from homeassistant.core import HomeAssistant, callback
30 from homeassistant.helpers.entity_platform import AddEntitiesCallback
31 from homeassistant.helpers.typing import StateType
32 
33 from . import AirGradientConfigEntry
34 from .const import PM_STANDARD, PM_STANDARD_REVERSE
35 from .coordinator import AirGradientCoordinator
36 from .entity import AirGradientEntity
37 
38 
39 @dataclass(frozen=True, kw_only=True)
41  """Describes AirGradient measurement sensor entity."""
42 
43  value_fn: Callable[[Measures], StateType]
44 
45 
46 @dataclass(frozen=True, kw_only=True)
48  """Describes AirGradient config sensor entity."""
49 
50  value_fn: Callable[[Config], StateType]
51 
52 
53 MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, ...] = (
55  key="pm01",
56  device_class=SensorDeviceClass.PM1,
57  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
58  state_class=SensorStateClass.MEASUREMENT,
59  value_fn=lambda status: status.pm01,
60  ),
62  key="pm02",
63  device_class=SensorDeviceClass.PM25,
64  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
65  state_class=SensorStateClass.MEASUREMENT,
66  value_fn=lambda status: status.pm02,
67  ),
69  key="pm10",
70  device_class=SensorDeviceClass.PM10,
71  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
72  state_class=SensorStateClass.MEASUREMENT,
73  value_fn=lambda status: status.pm10,
74  ),
76  key="temperature",
77  device_class=SensorDeviceClass.TEMPERATURE,
78  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
79  state_class=SensorStateClass.MEASUREMENT,
80  value_fn=lambda status: status.ambient_temperature,
81  ),
83  key="humidity",
84  device_class=SensorDeviceClass.HUMIDITY,
85  native_unit_of_measurement=PERCENTAGE,
86  state_class=SensorStateClass.MEASUREMENT,
87  value_fn=lambda status: status.relative_humidity,
88  ),
90  key="signal_strength",
91  device_class=SensorDeviceClass.SIGNAL_STRENGTH,
92  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
93  entity_category=EntityCategory.DIAGNOSTIC,
94  state_class=SensorStateClass.MEASUREMENT,
95  entity_registry_enabled_default=False,
96  value_fn=lambda status: status.signal_strength,
97  ),
99  key="tvoc",
100  translation_key="total_volatile_organic_component_index",
101  state_class=SensorStateClass.MEASUREMENT,
102  value_fn=lambda status: status.total_volatile_organic_component_index,
103  ),
105  key="nitrogen_index",
106  translation_key="nitrogen_index",
107  state_class=SensorStateClass.MEASUREMENT,
108  value_fn=lambda status: status.nitrogen_index,
109  ),
111  key="co2",
112  device_class=SensorDeviceClass.CO2,
113  native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
114  state_class=SensorStateClass.MEASUREMENT,
115  value_fn=lambda status: status.rco2,
116  ),
118  key="pm003",
119  translation_key="pm003_count",
120  native_unit_of_measurement="particles/dL",
121  state_class=SensorStateClass.MEASUREMENT,
122  value_fn=lambda status: status.pm003_count,
123  ),
125  key="nox_raw",
126  translation_key="raw_nitrogen",
127  native_unit_of_measurement="ticks",
128  state_class=SensorStateClass.MEASUREMENT,
129  entity_registry_enabled_default=False,
130  value_fn=lambda status: status.raw_nitrogen,
131  ),
133  key="tvoc_raw",
134  translation_key="raw_total_volatile_organic_component",
135  native_unit_of_measurement="ticks",
136  state_class=SensorStateClass.MEASUREMENT,
137  entity_registry_enabled_default=False,
138  value_fn=lambda status: status.raw_total_volatile_organic_component,
139  ),
140 )
141 
142 CONFIG_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (
144  key="co2_automatic_baseline_calibration_days",
145  translation_key="co2_automatic_baseline_calibration_days",
146  device_class=SensorDeviceClass.DURATION,
147  native_unit_of_measurement=UnitOfTime.DAYS,
148  entity_category=EntityCategory.DIAGNOSTIC,
149  value_fn=lambda config: config.co2_automatic_baseline_calibration_days,
150  ),
152  key="nox_learning_offset",
153  translation_key="nox_learning_offset",
154  device_class=SensorDeviceClass.DURATION,
155  native_unit_of_measurement=UnitOfTime.DAYS,
156  entity_category=EntityCategory.DIAGNOSTIC,
157  value_fn=lambda config: config.nox_learning_offset,
158  ),
160  key="tvoc_learning_offset",
161  translation_key="tvoc_learning_offset",
162  device_class=SensorDeviceClass.DURATION,
163  native_unit_of_measurement=UnitOfTime.DAYS,
164  entity_category=EntityCategory.DIAGNOSTIC,
165  value_fn=lambda config: config.tvoc_learning_offset,
166  ),
167 )
168 
169 CONFIG_LED_BAR_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (
171  key="led_bar_mode",
172  translation_key="led_bar_mode",
173  device_class=SensorDeviceClass.ENUM,
174  options=[x.value for x in LedBarMode],
175  entity_category=EntityCategory.DIAGNOSTIC,
176  value_fn=lambda config: config.led_bar_mode,
177  ),
179  key="led_bar_brightness",
180  translation_key="led_bar_brightness",
181  native_unit_of_measurement=PERCENTAGE,
182  entity_category=EntityCategory.DIAGNOSTIC,
183  value_fn=lambda config: config.led_bar_brightness,
184  ),
185 )
186 
187 CONFIG_DISPLAY_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (
189  key="display_temperature_unit",
190  translation_key="display_temperature_unit",
191  device_class=SensorDeviceClass.ENUM,
192  options=[x.value for x in TemperatureUnit],
193  entity_category=EntityCategory.DIAGNOSTIC,
194  value_fn=lambda config: config.temperature_unit,
195  ),
197  key="display_pm_standard",
198  translation_key="display_pm_standard",
199  device_class=SensorDeviceClass.ENUM,
200  options=list(PM_STANDARD_REVERSE),
201  entity_category=EntityCategory.DIAGNOSTIC,
202  value_fn=lambda config: PM_STANDARD.get(config.pm_standard),
203  ),
205  key="display_brightness",
206  translation_key="display_brightness",
207  native_unit_of_measurement=PERCENTAGE,
208  entity_category=EntityCategory.DIAGNOSTIC,
209  value_fn=lambda config: config.display_brightness,
210  ),
211 )
212 
213 
215  hass: HomeAssistant,
216  entry: AirGradientConfigEntry,
217  async_add_entities: AddEntitiesCallback,
218 ) -> None:
219  """Set up AirGradient sensor entities based on a config entry."""
220 
221  coordinator = entry.runtime_data
222  listener: Callable[[], None] | None = None
223  not_setup: set[AirGradientMeasurementSensorEntityDescription] = set(
224  MEASUREMENT_SENSOR_TYPES
225  )
226 
227  @callback
228  def add_entities() -> None:
229  """Add new entities based on the latest data."""
230  nonlocal not_setup, listener
231  sensor_descriptions = not_setup
232  not_setup = set()
233  sensors = []
234  for description in sensor_descriptions:
235  if description.value_fn(coordinator.data.measures) is None:
236  not_setup.add(description)
237  else:
238  sensors.append(AirGradientMeasurementSensor(coordinator, description))
239 
240  if sensors:
241  async_add_entities(sensors)
242  if not_setup:
243  if not listener:
244  listener = coordinator.async_add_listener(add_entities)
245  elif listener:
246  listener()
247 
248  add_entities()
249 
250  entities = [
251  AirGradientConfigSensor(coordinator, description)
252  for description in CONFIG_SENSOR_TYPES
253  ]
254  if "L" in coordinator.data.measures.model:
255  entities.extend(
256  AirGradientConfigSensor(coordinator, description)
257  for description in CONFIG_LED_BAR_SENSOR_TYPES
258  )
259  if "I" in coordinator.data.measures.model:
260  entities.extend(
261  AirGradientConfigSensor(coordinator, description)
262  for description in CONFIG_DISPLAY_SENSOR_TYPES
263  )
264  async_add_entities(entities)
265 
266 
268  """Defines an AirGradient sensor."""
269 
270  def __init__(
271  self,
272  coordinator: AirGradientCoordinator,
273  description: SensorEntityDescription,
274  ) -> None:
275  """Initialize airgradient sensor."""
276  super().__init__(coordinator)
277  self.entity_descriptionentity_description = description
278  self._attr_unique_id_attr_unique_id = f"{coordinator.serial_number}-{description.key}"
279 
280 
282  """Defines an AirGradient sensor."""
283 
284  entity_description: AirGradientMeasurementSensorEntityDescription
285 
286  @property
287  def native_value(self) -> StateType:
288  """Return the state of the sensor."""
289  return self.entity_descriptionentity_description.value_fn(self.coordinator.data.measures)
290 
291 
293  """Defines an AirGradient sensor."""
294 
295  entity_description: AirGradientConfigSensorEntityDescription
296 
297  def __init__(
298  self,
299  coordinator: AirGradientCoordinator,
300  description: AirGradientConfigSensorEntityDescription,
301  ) -> None:
302  """Initialize airgradient sensor."""
303  super().__init__(coordinator, description)
304  self._attr_entity_registry_enabled_default_attr_entity_registry_enabled_default = (
305  coordinator.data.config.configuration_control
306  is not ConfigurationControl.LOCAL
307  )
308 
309  @property
310  def native_value(self) -> StateType:
311  """Return the state of the sensor."""
312  return self.entity_descriptionentity_description.value_fn(self.coordinator.data.config)
None __init__(self, AirGradientCoordinator coordinator, AirGradientConfigSensorEntityDescription description)
Definition: sensor.py:301
None __init__(self, AirGradientCoordinator coordinator, SensorEntityDescription description)
Definition: sensor.py:274
None async_setup_entry(HomeAssistant hass, AirGradientConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:218
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40