Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for the AEMET OpenData service."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime
8 from typing import Final
9 
10 from aemet_opendata.const import (
11  AOD_CONDITION,
12  AOD_FEEL_TEMP,
13  AOD_FORECAST_CURRENT,
14  AOD_FORECAST_DAILY,
15  AOD_FORECAST_HOURLY,
16  AOD_HUMIDITY,
17  AOD_ID,
18  AOD_NAME,
19  AOD_PRECIPITATION,
20  AOD_PRECIPITATION_PROBABILITY,
21  AOD_PRESSURE,
22  AOD_RAIN,
23  AOD_RAIN_PROBABILITY,
24  AOD_SNOW,
25  AOD_SNOW_PROBABILITY,
26  AOD_STATION,
27  AOD_STORM_PROBABILITY,
28  AOD_TEMP,
29  AOD_TEMP_MAX,
30  AOD_TEMP_MIN,
31  AOD_TIMESTAMP_UTC,
32  AOD_TOWN,
33  AOD_WEATHER,
34  AOD_WIND_DIRECTION,
35  AOD_WIND_SPEED,
36  AOD_WIND_SPEED_MAX,
37 )
38 from aemet_opendata.helpers import dict_nested_value
39 
41  SensorDeviceClass,
42  SensorEntity,
43  SensorEntityDescription,
44  SensorStateClass,
45 )
46 from homeassistant.const import (
47  DEGREE,
48  PERCENTAGE,
49  UnitOfPressure,
50  UnitOfSpeed,
51  UnitOfTemperature,
52  UnitOfVolumetricFlux,
53 )
54 from homeassistant.core import HomeAssistant
55 from homeassistant.helpers.entity_platform import AddEntitiesCallback
56 from homeassistant.util import dt as dt_util
57 
58 from .const import (
59  ATTR_API_CONDITION,
60  ATTR_API_FORECAST_CONDITION,
61  ATTR_API_FORECAST_PRECIPITATION,
62  ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
63  ATTR_API_FORECAST_TEMP,
64  ATTR_API_FORECAST_TEMP_LOW,
65  ATTR_API_FORECAST_TIME,
66  ATTR_API_FORECAST_WIND_BEARING,
67  ATTR_API_FORECAST_WIND_MAX_SPEED,
68  ATTR_API_FORECAST_WIND_SPEED,
69  ATTR_API_HUMIDITY,
70  ATTR_API_PRESSURE,
71  ATTR_API_RAIN,
72  ATTR_API_RAIN_PROB,
73  ATTR_API_SNOW,
74  ATTR_API_SNOW_PROB,
75  ATTR_API_STATION_ID,
76  ATTR_API_STATION_NAME,
77  ATTR_API_STATION_TIMESTAMP,
78  ATTR_API_STORM_PROB,
79  ATTR_API_TEMPERATURE,
80  ATTR_API_TEMPERATURE_FEELING,
81  ATTR_API_TOWN_ID,
82  ATTR_API_TOWN_NAME,
83  ATTR_API_TOWN_TIMESTAMP,
84  ATTR_API_WIND_BEARING,
85  ATTR_API_WIND_MAX_SPEED,
86  ATTR_API_WIND_SPEED,
87  CONDITIONS_MAP,
88 )
89 from .coordinator import AemetConfigEntry, WeatherUpdateCoordinator
90 from .entity import AemetEntity
91 
92 
93 @dataclass(frozen=True, kw_only=True)
95  """A class that describes AEMET OpenData sensor entities."""
96 
97  keys: list[str] | None = None
98  value_fn: Callable[[str], datetime | float | int | str | None] = lambda value: value
99 
100 
101 FORECAST_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
103  key=f"forecast-daily-{ATTR_API_FORECAST_CONDITION}",
104  keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_CONDITION],
105  name="Daily forecast condition",
106  value_fn=CONDITIONS_MAP.get,
107  ),
109  entity_registry_enabled_default=False,
110  key=f"forecast-hourly-{ATTR_API_FORECAST_CONDITION}",
111  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_CONDITION],
112  name="Hourly forecast condition",
113  value_fn=CONDITIONS_MAP.get,
114  ),
116  entity_registry_enabled_default=False,
117  key=f"forecast-hourly-{ATTR_API_FORECAST_PRECIPITATION}",
118  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_PRECIPITATION],
119  name="Hourly forecast precipitation",
120  native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
121  device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
122  ),
124  key=f"forecast-daily-{ATTR_API_FORECAST_PRECIPITATION_PROBABILITY}",
125  keys=[
126  AOD_TOWN,
127  AOD_FORECAST_DAILY,
128  AOD_FORECAST_CURRENT,
129  AOD_PRECIPITATION_PROBABILITY,
130  ],
131  name="Daily forecast precipitation probability",
132  native_unit_of_measurement=PERCENTAGE,
133  ),
135  entity_registry_enabled_default=False,
136  key=f"forecast-hourly-{ATTR_API_FORECAST_PRECIPITATION_PROBABILITY}",
137  keys=[
138  AOD_TOWN,
139  AOD_FORECAST_HOURLY,
140  AOD_FORECAST_CURRENT,
141  AOD_PRECIPITATION_PROBABILITY,
142  ],
143  name="Hourly forecast precipitation probability",
144  native_unit_of_measurement=PERCENTAGE,
145  ),
147  key=f"forecast-daily-{ATTR_API_FORECAST_TEMP}",
148  keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_TEMP_MAX],
149  name="Daily forecast temperature",
150  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
151  device_class=SensorDeviceClass.TEMPERATURE,
152  ),
154  key=f"forecast-daily-{ATTR_API_FORECAST_TEMP_LOW}",
155  keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_TEMP_MIN],
156  name="Daily forecast temperature low",
157  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
158  device_class=SensorDeviceClass.TEMPERATURE,
159  ),
161  entity_registry_enabled_default=False,
162  key=f"forecast-hourly-{ATTR_API_FORECAST_TEMP}",
163  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_TEMP],
164  name="Hourly forecast temperature",
165  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
166  device_class=SensorDeviceClass.TEMPERATURE,
167  ),
169  key=f"forecast-daily-{ATTR_API_FORECAST_TIME}",
170  keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_TIMESTAMP_UTC],
171  name="Daily forecast time",
172  device_class=SensorDeviceClass.TIMESTAMP,
173  value_fn=dt_util.parse_datetime,
174  ),
176  entity_registry_enabled_default=False,
177  key=f"forecast-hourly-{ATTR_API_FORECAST_TIME}",
178  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_TIMESTAMP_UTC],
179  name="Hourly forecast time",
180  device_class=SensorDeviceClass.TIMESTAMP,
181  value_fn=dt_util.parse_datetime,
182  ),
184  key=f"forecast-daily-{ATTR_API_FORECAST_WIND_BEARING}",
185  keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_WIND_DIRECTION],
186  name="Daily forecast wind bearing",
187  native_unit_of_measurement=DEGREE,
188  ),
190  entity_registry_enabled_default=False,
191  key=f"forecast-hourly-{ATTR_API_FORECAST_WIND_BEARING}",
192  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_DIRECTION],
193  name="Hourly forecast wind bearing",
194  native_unit_of_measurement=DEGREE,
195  ),
197  entity_registry_enabled_default=False,
198  key=f"forecast-hourly-{ATTR_API_FORECAST_WIND_MAX_SPEED}",
199  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_SPEED_MAX],
200  name="Hourly forecast wind max speed",
201  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
202  device_class=SensorDeviceClass.WIND_SPEED,
203  ),
205  key=f"forecast-daily-{ATTR_API_FORECAST_WIND_SPEED}",
206  keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_WIND_SPEED],
207  name="Daily forecast wind speed",
208  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
209  device_class=SensorDeviceClass.WIND_SPEED,
210  ),
212  entity_registry_enabled_default=False,
213  key=f"forecast-hourly-{ATTR_API_FORECAST_WIND_SPEED}",
214  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_SPEED],
215  name="Hourly forecast wind speed",
216  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
217  device_class=SensorDeviceClass.WIND_SPEED,
218  ),
219 )
220 
221 
222 WEATHER_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
224  key=ATTR_API_CONDITION,
225  keys=[AOD_WEATHER, AOD_CONDITION],
226  name="Condition",
227  value_fn=CONDITIONS_MAP.get,
228  ),
230  key=ATTR_API_HUMIDITY,
231  keys=[AOD_WEATHER, AOD_HUMIDITY],
232  name="Humidity",
233  native_unit_of_measurement=PERCENTAGE,
234  device_class=SensorDeviceClass.HUMIDITY,
235  state_class=SensorStateClass.MEASUREMENT,
236  ),
238  key=ATTR_API_PRESSURE,
239  keys=[AOD_WEATHER, AOD_PRESSURE],
240  name="Pressure",
241  native_unit_of_measurement=UnitOfPressure.HPA,
242  device_class=SensorDeviceClass.PRESSURE,
243  state_class=SensorStateClass.MEASUREMENT,
244  ),
246  key=ATTR_API_RAIN,
247  keys=[AOD_WEATHER, AOD_RAIN],
248  name="Rain",
249  native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
250  device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
251  state_class=SensorStateClass.MEASUREMENT,
252  ),
254  key=ATTR_API_RAIN_PROB,
255  keys=[AOD_WEATHER, AOD_RAIN_PROBABILITY],
256  name="Rain probability",
257  native_unit_of_measurement=PERCENTAGE,
258  state_class=SensorStateClass.MEASUREMENT,
259  ),
261  key=ATTR_API_SNOW,
262  keys=[AOD_WEATHER, AOD_SNOW],
263  name="Snow",
264  native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
265  device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
266  state_class=SensorStateClass.MEASUREMENT,
267  ),
269  key=ATTR_API_SNOW_PROB,
270  keys=[AOD_WEATHER, AOD_SNOW_PROBABILITY],
271  name="Snow probability",
272  native_unit_of_measurement=PERCENTAGE,
273  state_class=SensorStateClass.MEASUREMENT,
274  ),
276  key=ATTR_API_STATION_ID,
277  keys=[AOD_STATION, AOD_ID],
278  name="Station ID",
279  ),
281  key=ATTR_API_STATION_NAME,
282  keys=[AOD_STATION, AOD_NAME],
283  name="Station name",
284  ),
286  key=ATTR_API_STATION_TIMESTAMP,
287  keys=[AOD_STATION, AOD_TIMESTAMP_UTC],
288  name="Station timestamp",
289  device_class=SensorDeviceClass.TIMESTAMP,
290  value_fn=dt_util.parse_datetime,
291  ),
293  key=ATTR_API_STORM_PROB,
294  keys=[AOD_WEATHER, AOD_STORM_PROBABILITY],
295  name="Storm probability",
296  native_unit_of_measurement=PERCENTAGE,
297  state_class=SensorStateClass.MEASUREMENT,
298  ),
300  key=ATTR_API_TEMPERATURE,
301  keys=[AOD_WEATHER, AOD_TEMP],
302  name="Temperature",
303  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
304  device_class=SensorDeviceClass.TEMPERATURE,
305  state_class=SensorStateClass.MEASUREMENT,
306  ),
308  key=ATTR_API_TEMPERATURE_FEELING,
309  keys=[AOD_WEATHER, AOD_FEEL_TEMP],
310  name="Temperature feeling",
311  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
312  device_class=SensorDeviceClass.TEMPERATURE,
313  state_class=SensorStateClass.MEASUREMENT,
314  ),
316  key=ATTR_API_TOWN_ID,
317  keys=[AOD_TOWN, AOD_ID],
318  name="Town ID",
319  ),
321  key=ATTR_API_TOWN_NAME,
322  keys=[AOD_TOWN, AOD_NAME],
323  name="Town name",
324  ),
326  key=ATTR_API_TOWN_TIMESTAMP,
327  keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_TIMESTAMP_UTC],
328  name="Town timestamp",
329  device_class=SensorDeviceClass.TIMESTAMP,
330  value_fn=dt_util.parse_datetime,
331  ),
333  key=ATTR_API_WIND_BEARING,
334  keys=[AOD_WEATHER, AOD_WIND_DIRECTION],
335  name="Wind bearing",
336  native_unit_of_measurement=DEGREE,
337  state_class=SensorStateClass.MEASUREMENT,
338  ),
340  key=ATTR_API_WIND_MAX_SPEED,
341  keys=[AOD_WEATHER, AOD_WIND_SPEED_MAX],
342  name="Wind max speed",
343  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
344  device_class=SensorDeviceClass.WIND_SPEED,
345  state_class=SensorStateClass.MEASUREMENT,
346  ),
348  key=ATTR_API_WIND_SPEED,
349  keys=[AOD_WEATHER, AOD_WIND_SPEED],
350  name="Wind speed",
351  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
352  device_class=SensorDeviceClass.WIND_SPEED,
353  state_class=SensorStateClass.MEASUREMENT,
354  ),
355 )
356 
357 
359  hass: HomeAssistant,
360  config_entry: AemetConfigEntry,
361  async_add_entities: AddEntitiesCallback,
362 ) -> None:
363  """Set up AEMET OpenData sensor entities based on a config entry."""
364  domain_data = config_entry.runtime_data
365  name = domain_data.name
366  coordinator = domain_data.coordinator
367 
368  unique_id = config_entry.unique_id
369  assert unique_id is not None
370 
372  AemetSensor(
373  name,
374  coordinator,
375  description,
376  unique_id,
377  )
378  for description in FORECAST_SENSORS + WEATHER_SENSORS
379  if dict_nested_value(coordinator.data["lib"], description.keys) is not None
380  )
381 
382 
384  """Implementation of an AEMET OpenData sensor."""
385 
386  entity_description: AemetSensorEntityDescription
387 
388  def __init__(
389  self,
390  name: str,
391  coordinator: WeatherUpdateCoordinator,
392  description: AemetSensorEntityDescription,
393  unique_id: str,
394  ) -> None:
395  """Initialize the sensor."""
396  super().__init__(coordinator, name, unique_id)
397  self.entity_descriptionentity_description = description
398  self._attr_unique_id_attr_unique_id = f"{unique_id}-{description.key}"
399 
400  @property
401  def native_value(self):
402  """Return the state of the device."""
403  value = self.get_aemet_valueget_aemet_value(self.entity_descriptionentity_description.keys)
404  return self.entity_descriptionentity_description.value_fn(value)
Any get_aemet_value(self, list[str] keys)
Definition: entity.py:43
None __init__(self, str name, WeatherUpdateCoordinator coordinator, AemetSensorEntityDescription description, str unique_id)
Definition: sensor.py:394
None async_setup_entry(HomeAssistant hass, AemetConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:362