Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for the GIOS service."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 import logging
8 
9 from gios.model import GiosSensors
10 
12  DOMAIN as PLATFORM,
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16  SensorStateClass,
17 )
18 from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONF_NAME
19 from homeassistant.core import HomeAssistant
20 from homeassistant.helpers import entity_registry as er
21 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
22 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23 from homeassistant.helpers.typing import StateType
24 from homeassistant.helpers.update_coordinator import CoordinatorEntity
25 
26 from . import GiosConfigEntry
27 from .const import (
28  ATTR_AQI,
29  ATTR_C6H6,
30  ATTR_CO,
31  ATTR_NO2,
32  ATTR_O3,
33  ATTR_PM10,
34  ATTR_PM25,
35  ATTR_SO2,
36  ATTRIBUTION,
37  DOMAIN,
38  MANUFACTURER,
39  URL,
40 )
41 from .coordinator import GiosDataUpdateCoordinator
42 
43 _LOGGER = logging.getLogger(__name__)
44 
45 
46 @dataclass(frozen=True, kw_only=True)
48  """Class describing GIOS sensor entities."""
49 
50  value: Callable[[GiosSensors], StateType]
51  subkey: str | None = None
52 
53 
54 SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
56  key=ATTR_AQI,
57  value=lambda sensors: sensors.aqi.value if sensors.aqi else None,
58  device_class=SensorDeviceClass.ENUM,
59  options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
60  translation_key="aqi",
61  ),
63  key=ATTR_C6H6,
64  value=lambda sensors: sensors.c6h6.value if sensors.c6h6 else None,
65  suggested_display_precision=0,
66  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
67  state_class=SensorStateClass.MEASUREMENT,
68  translation_key="c6h6",
69  ),
71  key=ATTR_CO,
72  value=lambda sensors: sensors.co.value if sensors.co else None,
73  suggested_display_precision=0,
74  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
75  state_class=SensorStateClass.MEASUREMENT,
76  translation_key="co",
77  ),
79  key=ATTR_NO2,
80  value=lambda sensors: sensors.no2.value if sensors.no2 else None,
81  suggested_display_precision=0,
82  device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
83  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
84  state_class=SensorStateClass.MEASUREMENT,
85  ),
87  key=ATTR_NO2,
88  subkey="index",
89  value=lambda sensors: sensors.no2.index if sensors.no2 else None,
90  device_class=SensorDeviceClass.ENUM,
91  options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
92  translation_key="no2_index",
93  ),
95  key=ATTR_O3,
96  value=lambda sensors: sensors.o3.value if sensors.o3 else None,
97  suggested_display_precision=0,
98  device_class=SensorDeviceClass.OZONE,
99  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
100  state_class=SensorStateClass.MEASUREMENT,
101  ),
103  key=ATTR_O3,
104  subkey="index",
105  value=lambda sensors: sensors.o3.index if sensors.o3 else None,
106  device_class=SensorDeviceClass.ENUM,
107  options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
108  translation_key="o3_index",
109  ),
111  key=ATTR_PM10,
112  value=lambda sensors: sensors.pm10.value if sensors.pm10 else None,
113  suggested_display_precision=0,
114  device_class=SensorDeviceClass.PM10,
115  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
116  state_class=SensorStateClass.MEASUREMENT,
117  ),
119  key=ATTR_PM10,
120  subkey="index",
121  value=lambda sensors: sensors.pm10.index if sensors.pm10 else None,
122  device_class=SensorDeviceClass.ENUM,
123  options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
124  translation_key="pm10_index",
125  ),
127  key=ATTR_PM25,
128  value=lambda sensors: sensors.pm25.value if sensors.pm25 else None,
129  suggested_display_precision=0,
130  device_class=SensorDeviceClass.PM25,
131  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
132  state_class=SensorStateClass.MEASUREMENT,
133  ),
135  key=ATTR_PM25,
136  subkey="index",
137  value=lambda sensors: sensors.pm25.index if sensors.pm25 else None,
138  device_class=SensorDeviceClass.ENUM,
139  options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
140  translation_key="pm25_index",
141  ),
143  key=ATTR_SO2,
144  value=lambda sensors: sensors.so2.value if sensors.so2 else None,
145  suggested_display_precision=0,
146  device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
147  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
148  state_class=SensorStateClass.MEASUREMENT,
149  ),
151  key=ATTR_SO2,
152  subkey="index",
153  value=lambda sensors: sensors.so2.index if sensors.so2 else None,
154  device_class=SensorDeviceClass.ENUM,
155  options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
156  translation_key="so2_index",
157  ),
158 )
159 
160 
162  hass: HomeAssistant, entry: GiosConfigEntry, async_add_entities: AddEntitiesCallback
163 ) -> None:
164  """Add a GIOS entities from a config_entry."""
165  name = entry.data[CONF_NAME]
166 
167  coordinator = entry.runtime_data.coordinator
168  # Due to the change of the attribute name of one sensor, it is necessary to migrate
169  # the unique_id to the new name.
170  entity_registry = er.async_get(hass)
171  old_unique_id = f"{coordinator.gios.station_id}-pm2.5"
172  if entity_id := entity_registry.async_get_entity_id(
173  PLATFORM, DOMAIN, old_unique_id
174  ):
175  new_unique_id = f"{coordinator.gios.station_id}-{ATTR_PM25}"
176  _LOGGER.debug(
177  "Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
178  entity_id,
179  old_unique_id,
180  new_unique_id,
181  )
182  entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id)
183 
184  sensors: list[GiosSensor] = []
185 
186  for description in SENSOR_TYPES:
187  if getattr(coordinator.data, description.key) is None:
188  continue
189  sensors.append(GiosSensor(name, coordinator, description))
190 
191  async_add_entities(sensors)
192 
193 
194 class GiosSensor(CoordinatorEntity[GiosDataUpdateCoordinator], SensorEntity):
195  """Define an GIOS sensor."""
196 
197  _attr_attribution = ATTRIBUTION
198  _attr_has_entity_name = True
199  entity_description: GiosSensorEntityDescription
200 
201  def __init__(
202  self,
203  name: str,
204  coordinator: GiosDataUpdateCoordinator,
205  description: GiosSensorEntityDescription,
206  ) -> None:
207  """Initialize."""
208  super().__init__(coordinator)
209  self._attr_device_info_attr_device_info = DeviceInfo(
210  entry_type=DeviceEntryType.SERVICE,
211  identifiers={(DOMAIN, str(coordinator.gios.station_id))},
212  manufacturer=MANUFACTURER,
213  name=name,
214  configuration_url=URL.format(station_id=coordinator.gios.station_id),
215  )
216  if description.subkey:
217  self._attr_unique_id_attr_unique_id = (
218  f"{coordinator.gios.station_id}-{description.key}-{description.subkey}"
219  )
220  else:
221  self._attr_unique_id_attr_unique_id = f"{coordinator.gios.station_id}-{description.key}"
222  self.entity_descriptionentity_description = description
223 
224  @property
225  def native_value(self) -> StateType:
226  """Return the state."""
227  return self.entity_descriptionentity_description.value(self.coordinator.data)
228 
229  @property
230  def available(self) -> bool:
231  """Return if entity is available."""
232  sensor_data = getattr(self.coordinator.data, self.entity_descriptionentity_description.key)
233  available = super().available and bool(sensor_data)
234 
235  # Sometimes the API returns sensor data without indexes
236  if self.entity_descriptionentity_description.subkey and available:
237  return available and bool(sensor_data.index)
238 
239  return available
None __init__(self, str name, GiosDataUpdateCoordinator coordinator, GiosSensorEntityDescription description)
Definition: sensor.py:206
None async_setup_entry(HomeAssistant hass, GiosConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:163