Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for the AccuWeather service."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import Any, cast
8 
10  SensorDeviceClass,
11  SensorEntity,
12  SensorEntityDescription,
13  SensorStateClass,
14 )
15 from homeassistant.const import (
16  CONCENTRATION_PARTS_PER_CUBIC_METER,
17  PERCENTAGE,
18  UV_INDEX,
19  UnitOfIrradiance,
20  UnitOfLength,
21  UnitOfPressure,
22  UnitOfSpeed,
23  UnitOfTemperature,
24  UnitOfTime,
25  UnitOfVolumetricFlux,
26 )
27 from homeassistant.core import HomeAssistant, callback
28 from homeassistant.helpers.entity_platform import AddEntitiesCallback
29 from homeassistant.helpers.update_coordinator import CoordinatorEntity
30 
31 from .const import (
32  API_METRIC,
33  ATTR_CATEGORY,
34  ATTR_DIRECTION,
35  ATTR_ENGLISH,
36  ATTR_LEVEL,
37  ATTR_SPEED,
38  ATTR_VALUE,
39  ATTRIBUTION,
40  MAX_FORECAST_DAYS,
41 )
42 from .coordinator import (
43  AccuWeatherConfigEntry,
44  AccuWeatherDailyForecastDataUpdateCoordinator,
45  AccuWeatherObservationDataUpdateCoordinator,
46 )
47 
48 PARALLEL_UPDATES = 1
49 
50 
51 @dataclass(frozen=True, kw_only=True)
53  """Class describing AccuWeather sensor entities."""
54 
55  value_fn: Callable[[dict[str, Any]], str | int | float | None]
56  attr_fn: Callable[[dict[str, Any]], dict[str, Any]] = lambda _: {}
57 
58 
59 FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
61  key="AirQuality",
62  value_fn=lambda data: cast(str, data[ATTR_CATEGORY]),
63  device_class=SensorDeviceClass.ENUM,
64  options=["good", "hazardous", "high", "low", "moderate", "unhealthy"],
65  translation_key="air_quality",
66  ),
68  key="CloudCoverDay",
69  entity_registry_enabled_default=False,
70  native_unit_of_measurement=PERCENTAGE,
71  value_fn=lambda data: cast(int, data),
72  translation_key="cloud_cover_day",
73  ),
75  key="CloudCoverNight",
76  entity_registry_enabled_default=False,
77  native_unit_of_measurement=PERCENTAGE,
78  value_fn=lambda data: cast(int, data),
79  translation_key="cloud_cover_night",
80  ),
82  key="Grass",
83  entity_registry_enabled_default=False,
84  native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
85  value_fn=lambda data: cast(int, data[ATTR_VALUE]),
86  attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
87  translation_key="grass_pollen",
88  ),
90  key="HoursOfSun",
91  native_unit_of_measurement=UnitOfTime.HOURS,
92  value_fn=lambda data: cast(float, data),
93  translation_key="hours_of_sun",
94  ),
96  key="LongPhraseDay",
97  value_fn=lambda data: cast(str, data),
98  translation_key="condition_day",
99  ),
101  key="LongPhraseNight",
102  value_fn=lambda data: cast(str, data),
103  translation_key="condition_night",
104  ),
106  key="Mold",
107  entity_registry_enabled_default=False,
108  native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
109  value_fn=lambda data: cast(int, data[ATTR_VALUE]),
110  attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
111  translation_key="mold_pollen",
112  ),
114  key="Ragweed",
115  native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
116  entity_registry_enabled_default=False,
117  value_fn=lambda data: cast(int, data[ATTR_VALUE]),
118  attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
119  translation_key="ragweed_pollen",
120  ),
122  key="RealFeelTemperatureMax",
123  device_class=SensorDeviceClass.TEMPERATURE,
124  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
125  value_fn=lambda data: cast(float, data[ATTR_VALUE]),
126  translation_key="realfeel_temperature_max",
127  ),
129  key="RealFeelTemperatureMin",
130  device_class=SensorDeviceClass.TEMPERATURE,
131  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
132  value_fn=lambda data: cast(float, data[ATTR_VALUE]),
133  translation_key="realfeel_temperature_min",
134  ),
136  key="RealFeelTemperatureShadeMax",
137  device_class=SensorDeviceClass.TEMPERATURE,
138  entity_registry_enabled_default=False,
139  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
140  value_fn=lambda data: cast(float, data[ATTR_VALUE]),
141  translation_key="realfeel_temperature_shade_max",
142  ),
144  key="RealFeelTemperatureShadeMin",
145  device_class=SensorDeviceClass.TEMPERATURE,
146  entity_registry_enabled_default=False,
147  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
148  value_fn=lambda data: cast(float, data[ATTR_VALUE]),
149  translation_key="realfeel_temperature_shade_min",
150  ),
152  key="SolarIrradianceDay",
153  device_class=SensorDeviceClass.IRRADIANCE,
154  entity_registry_enabled_default=False,
155  native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
156  value_fn=lambda data: cast(float, data[ATTR_VALUE]),
157  translation_key="solar_irradiance_day",
158  ),
160  key="SolarIrradianceNight",
161  device_class=SensorDeviceClass.IRRADIANCE,
162  entity_registry_enabled_default=False,
163  native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
164  value_fn=lambda data: cast(float, data[ATTR_VALUE]),
165  translation_key="solar_irradiance_night",
166  ),
168  key="ThunderstormProbabilityDay",
169  native_unit_of_measurement=PERCENTAGE,
170  value_fn=lambda data: cast(int, data),
171  translation_key="thunderstorm_probability_day",
172  ),
174  key="ThunderstormProbabilityNight",
175  native_unit_of_measurement=PERCENTAGE,
176  value_fn=lambda data: cast(int, data),
177  translation_key="thunderstorm_probability_night",
178  ),
180  key="Tree",
181  native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
182  entity_registry_enabled_default=False,
183  value_fn=lambda data: cast(int, data[ATTR_VALUE]),
184  attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
185  translation_key="tree_pollen",
186  ),
188  key="UVIndex",
189  native_unit_of_measurement=UV_INDEX,
190  value_fn=lambda data: cast(int, data[ATTR_VALUE]),
191  attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
192  translation_key="uv_index_forecast",
193  ),
195  key="WindGustDay",
196  device_class=SensorDeviceClass.WIND_SPEED,
197  entity_registry_enabled_default=False,
198  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
199  value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
200  attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
201  translation_key="wind_gust_speed_day",
202  ),
204  key="WindGustNight",
205  device_class=SensorDeviceClass.WIND_SPEED,
206  entity_registry_enabled_default=False,
207  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
208  value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
209  attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
210  translation_key="wind_gust_speed_night",
211  ),
213  key="WindDay",
214  device_class=SensorDeviceClass.WIND_SPEED,
215  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
216  value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
217  attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
218  translation_key="wind_speed_day",
219  ),
221  key="WindNight",
222  device_class=SensorDeviceClass.WIND_SPEED,
223  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
224  value_fn=lambda data: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
225  attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
226  translation_key="wind_speed_night",
227  ),
228 )
229 
230 SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
232  key="ApparentTemperature",
233  device_class=SensorDeviceClass.TEMPERATURE,
234  entity_registry_enabled_default=False,
235  state_class=SensorStateClass.MEASUREMENT,
236  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
237  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
238  translation_key="apparent_temperature",
239  ),
241  key="Ceiling",
242  device_class=SensorDeviceClass.DISTANCE,
243  state_class=SensorStateClass.MEASUREMENT,
244  native_unit_of_measurement=UnitOfLength.METERS,
245  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
246  suggested_display_precision=0,
247  translation_key="cloud_ceiling",
248  ),
250  key="CloudCover",
251  entity_registry_enabled_default=False,
252  state_class=SensorStateClass.MEASUREMENT,
253  native_unit_of_measurement=PERCENTAGE,
254  value_fn=lambda data: cast(int, data),
255  translation_key="cloud_cover",
256  ),
258  key="DewPoint",
259  device_class=SensorDeviceClass.TEMPERATURE,
260  entity_registry_enabled_default=False,
261  state_class=SensorStateClass.MEASUREMENT,
262  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
263  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
264  translation_key="dew_point",
265  ),
267  key="RealFeelTemperature",
268  device_class=SensorDeviceClass.TEMPERATURE,
269  state_class=SensorStateClass.MEASUREMENT,
270  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
271  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
272  translation_key="realfeel_temperature",
273  ),
275  key="RealFeelTemperatureShade",
276  device_class=SensorDeviceClass.TEMPERATURE,
277  entity_registry_enabled_default=False,
278  state_class=SensorStateClass.MEASUREMENT,
279  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
280  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
281  translation_key="realfeel_temperature_shade",
282  ),
284  key="RelativeHumidity",
285  device_class=SensorDeviceClass.HUMIDITY,
286  entity_registry_enabled_default=False,
287  state_class=SensorStateClass.MEASUREMENT,
288  native_unit_of_measurement=PERCENTAGE,
289  value_fn=lambda data: cast(int, data),
290  translation_key="humidity",
291  ),
293  key="Precipitation",
294  device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
295  state_class=SensorStateClass.MEASUREMENT,
296  native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
297  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
298  attr_fn=lambda data: {"type": data["PrecipitationType"]},
299  translation_key="precipitation",
300  ),
302  key="Pressure",
303  device_class=SensorDeviceClass.PRESSURE,
304  entity_registry_enabled_default=False,
305  state_class=SensorStateClass.MEASUREMENT,
306  suggested_display_precision=0,
307  native_unit_of_measurement=UnitOfPressure.HPA,
308  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
309  translation_key="pressure",
310  ),
312  key="PressureTendency",
313  device_class=SensorDeviceClass.ENUM,
314  options=["falling", "rising", "steady"],
315  value_fn=lambda data: cast(str, data["LocalizedText"]).lower(),
316  translation_key="pressure_tendency",
317  ),
319  key="Temperature",
320  device_class=SensorDeviceClass.TEMPERATURE,
321  entity_registry_enabled_default=False,
322  state_class=SensorStateClass.MEASUREMENT,
323  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
324  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
325  translation_key="temperature",
326  ),
328  key="UVIndex",
329  state_class=SensorStateClass.MEASUREMENT,
330  entity_registry_enabled_default=False,
331  native_unit_of_measurement=UV_INDEX,
332  value_fn=lambda data: cast(int, data),
333  attr_fn=lambda data: {ATTR_LEVEL: data["UVIndexText"]},
334  translation_key="uv_index",
335  ),
337  key="WetBulbTemperature",
338  device_class=SensorDeviceClass.TEMPERATURE,
339  entity_registry_enabled_default=False,
340  state_class=SensorStateClass.MEASUREMENT,
341  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
342  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
343  translation_key="wet_bulb_temperature",
344  ),
346  key="WindChillTemperature",
347  device_class=SensorDeviceClass.TEMPERATURE,
348  entity_registry_enabled_default=False,
349  state_class=SensorStateClass.MEASUREMENT,
350  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
351  value_fn=lambda data: cast(float, data[API_METRIC][ATTR_VALUE]),
352  translation_key="wind_chill_temperature",
353  ),
355  key="Wind",
356  device_class=SensorDeviceClass.WIND_SPEED,
357  entity_registry_enabled_default=False,
358  state_class=SensorStateClass.MEASUREMENT,
359  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
360  value_fn=lambda data: cast(float, data[ATTR_SPEED][API_METRIC][ATTR_VALUE]),
361  translation_key="wind_speed",
362  ),
364  key="WindGust",
365  device_class=SensorDeviceClass.WIND_SPEED,
366  entity_registry_enabled_default=False,
367  state_class=SensorStateClass.MEASUREMENT,
368  native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
369  value_fn=lambda data: cast(float, data[ATTR_SPEED][API_METRIC][ATTR_VALUE]),
370  translation_key="wind_gust_speed",
371  ),
372 )
373 
374 
376  hass: HomeAssistant,
377  entry: AccuWeatherConfigEntry,
378  async_add_entities: AddEntitiesCallback,
379 ) -> None:
380  """Add AccuWeather entities from a config_entry."""
381  observation_coordinator: AccuWeatherObservationDataUpdateCoordinator = (
382  entry.runtime_data.coordinator_observation
383  )
384  forecast_daily_coordinator: AccuWeatherDailyForecastDataUpdateCoordinator = (
385  entry.runtime_data.coordinator_daily_forecast
386  )
387 
388  sensors: list[AccuWeatherSensor | AccuWeatherForecastSensor] = [
389  AccuWeatherSensor(observation_coordinator, description)
390  for description in SENSOR_TYPES
391  ]
392 
393  sensors.extend(
394  [
395  AccuWeatherForecastSensor(forecast_daily_coordinator, description, day)
396  for day in range(MAX_FORECAST_DAYS + 1)
397  for description in FORECAST_SENSOR_TYPES
398  if description.key in forecast_daily_coordinator.data[day]
399  ]
400  )
401 
402  async_add_entities(sensors)
403 
404 
406  CoordinatorEntity[AccuWeatherObservationDataUpdateCoordinator], SensorEntity
407 ):
408  """Define an AccuWeather entity."""
409 
410  _attr_attribution = ATTRIBUTION
411  _attr_has_entity_name = True
412  entity_description: AccuWeatherSensorDescription
413 
414  def __init__(
415  self,
416  coordinator: AccuWeatherObservationDataUpdateCoordinator,
417  description: AccuWeatherSensorDescription,
418  ) -> None:
419  """Initialize."""
420  super().__init__(coordinator)
421 
422  self.entity_descriptionentity_description = description
423  self._sensor_data_sensor_data = self._get_sensor_data_get_sensor_data(coordinator.data, description.key)
424  self._attr_unique_id_attr_unique_id = f"{coordinator.location_key}-{description.key}".lower()
425  self._attr_device_info_attr_device_info = coordinator.device_info
426 
427  @property
428  def native_value(self) -> str | int | float | None:
429  """Return the state."""
430  return self.entity_descriptionentity_description.value_fn(self._sensor_data_sensor_data)
431 
432  @property
433  def extra_state_attributes(self) -> dict[str, Any]:
434  """Return the state attributes."""
435  return self.entity_descriptionentity_description.attr_fn(self.coordinator.data)
436 
437  @callback
438  def _handle_coordinator_update(self) -> None:
439  """Handle data update."""
440  self._sensor_data_sensor_data = self._get_sensor_data_get_sensor_data(
441  self.coordinator.data, self.entity_descriptionentity_description.key
442  )
443  self.async_write_ha_stateasync_write_ha_state()
444 
445  @staticmethod
447  sensors: dict[str, Any],
448  kind: str,
449  ) -> Any:
450  """Get sensor data."""
451  if kind == "Precipitation":
452  return sensors["PrecipitationSummary"]["PastHour"]
453 
454  return sensors[kind]
455 
456 
458  CoordinatorEntity[AccuWeatherDailyForecastDataUpdateCoordinator], SensorEntity
459 ):
460  """Define an AccuWeather entity."""
461 
462  _attr_attribution = ATTRIBUTION
463  _attr_has_entity_name = True
464  entity_description: AccuWeatherSensorDescription
465 
466  def __init__(
467  self,
468  coordinator: AccuWeatherDailyForecastDataUpdateCoordinator,
469  description: AccuWeatherSensorDescription,
470  forecast_day: int,
471  ) -> None:
472  """Initialize."""
473  super().__init__(coordinator)
474 
475  self.entity_descriptionentity_description = description
476  self._sensor_data_sensor_data = self._get_sensor_data_get_sensor_data(
477  coordinator.data, description.key, forecast_day
478  )
479  self._attr_unique_id_attr_unique_id = (
480  f"{coordinator.location_key}-{description.key}-{forecast_day}".lower()
481  )
482  self._attr_device_info_attr_device_info = coordinator.device_info
483  self._attr_translation_placeholders_attr_translation_placeholders = {"forecast_day": str(forecast_day)}
484  self.forecast_dayforecast_day = forecast_day
485 
486  @property
487  def native_value(self) -> str | int | float | None:
488  """Return the state."""
489  return self.entity_descriptionentity_description.value_fn(self._sensor_data_sensor_data)
490 
491  @property
492  def extra_state_attributes(self) -> dict[str, Any]:
493  """Return the state attributes."""
494  return self.entity_descriptionentity_description.attr_fn(self._sensor_data_sensor_data)
495 
496  @callback
497  def _handle_coordinator_update(self) -> None:
498  """Handle data update."""
499  self._sensor_data_sensor_data = self._get_sensor_data_get_sensor_data(
500  self.coordinator.data, self.entity_descriptionentity_description.key, self.forecast_dayforecast_day
501  )
502  self.async_write_ha_stateasync_write_ha_state()
503 
504  @staticmethod
506  sensors: list[dict[str, Any]],
507  kind: str,
508  forecast_day: int,
509  ) -> Any:
510  """Get sensor data."""
511  return sensors[forecast_day][kind]
None __init__(self, AccuWeatherDailyForecastDataUpdateCoordinator coordinator, AccuWeatherSensorDescription description, int forecast_day)
Definition: sensor.py:471
Any _get_sensor_data(list[dict[str, Any]] sensors, str kind, int forecast_day)
Definition: sensor.py:509
None __init__(self, AccuWeatherObservationDataUpdateCoordinator coordinator, AccuWeatherSensorDescription description)
Definition: sensor.py:418
Any _get_sensor_data(dict[str, Any] sensors, str kind)
Definition: sensor.py:449
None async_setup_entry(HomeAssistant hass, AccuWeatherConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:379