Home Assistant Unofficial Reference 2024.12.1
weather.py
Go to the documentation of this file.
1 """Template platform that aggregates meteorological data."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import asdict, dataclass
6 from functools import partial
7 from typing import Any, Literal, Self
8 
9 import voluptuous as vol
10 
12  ATTR_CONDITION_CLEAR_NIGHT,
13  ATTR_CONDITION_CLOUDY,
14  ATTR_CONDITION_EXCEPTIONAL,
15  ATTR_CONDITION_FOG,
16  ATTR_CONDITION_HAIL,
17  ATTR_CONDITION_LIGHTNING,
18  ATTR_CONDITION_LIGHTNING_RAINY,
19  ATTR_CONDITION_PARTLYCLOUDY,
20  ATTR_CONDITION_POURING,
21  ATTR_CONDITION_RAINY,
22  ATTR_CONDITION_SNOWY,
23  ATTR_CONDITION_SNOWY_RAINY,
24  ATTR_CONDITION_SUNNY,
25  ATTR_CONDITION_WINDY,
26  ATTR_CONDITION_WINDY_VARIANT,
27  DOMAIN as WEATHER_DOMAIN,
28  ENTITY_ID_FORMAT,
29  PLATFORM_SCHEMA as WEATHER_PLATFORM_SCHEMA,
30  Forecast,
31  WeatherEntity,
32  WeatherEntityFeature,
33 )
34 from homeassistant.const import (
35  CONF_NAME,
36  CONF_TEMPERATURE_UNIT,
37  CONF_UNIQUE_ID,
38  STATE_UNAVAILABLE,
39  STATE_UNKNOWN,
40 )
41 from homeassistant.core import HomeAssistant, callback
42 from homeassistant.exceptions import TemplateError
43 from homeassistant.helpers import config_validation as cv, template
44 from homeassistant.helpers.entity import async_generate_entity_id
45 from homeassistant.helpers.entity_platform import AddEntitiesCallback
46 from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity
47 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
49  DistanceConverter,
50  PressureConverter,
51  SpeedConverter,
52  TemperatureConverter,
53 )
54 
55 from .coordinator import TriggerUpdateCoordinator
56 from .template_entity import TemplateEntity, rewrite_common_legacy_to_modern_conf
57 from .trigger_entity import TriggerEntity
58 
59 CHECK_FORECAST_KEYS = (
60  set()
61  .union(Forecast.__annotations__.keys())
62  # Manually add the forecast resulting attributes that only exists
63  # as native_* in the Forecast definition
64  .union(("apparent_temperature", "wind_gust_speed", "dew_point"))
65 )
66 
67 CONDITION_CLASSES = {
68  ATTR_CONDITION_CLEAR_NIGHT,
69  ATTR_CONDITION_CLOUDY,
70  ATTR_CONDITION_FOG,
71  ATTR_CONDITION_HAIL,
72  ATTR_CONDITION_LIGHTNING,
73  ATTR_CONDITION_LIGHTNING_RAINY,
74  ATTR_CONDITION_PARTLYCLOUDY,
75  ATTR_CONDITION_POURING,
76  ATTR_CONDITION_RAINY,
77  ATTR_CONDITION_SNOWY,
78  ATTR_CONDITION_SNOWY_RAINY,
79  ATTR_CONDITION_SUNNY,
80  ATTR_CONDITION_WINDY,
81  ATTR_CONDITION_WINDY_VARIANT,
82  ATTR_CONDITION_EXCEPTIONAL,
83 }
84 
85 CONF_WEATHER = "weather"
86 CONF_TEMPERATURE_TEMPLATE = "temperature_template"
87 CONF_HUMIDITY_TEMPLATE = "humidity_template"
88 CONF_CONDITION_TEMPLATE = "condition_template"
89 CONF_ATTRIBUTION_TEMPLATE = "attribution_template"
90 CONF_PRESSURE_TEMPLATE = "pressure_template"
91 CONF_WIND_SPEED_TEMPLATE = "wind_speed_template"
92 CONF_WIND_BEARING_TEMPLATE = "wind_bearing_template"
93 CONF_OZONE_TEMPLATE = "ozone_template"
94 CONF_VISIBILITY_TEMPLATE = "visibility_template"
95 CONF_FORECAST_DAILY_TEMPLATE = "forecast_daily_template"
96 CONF_FORECAST_HOURLY_TEMPLATE = "forecast_hourly_template"
97 CONF_FORECAST_TWICE_DAILY_TEMPLATE = "forecast_twice_daily_template"
98 CONF_PRESSURE_UNIT = "pressure_unit"
99 CONF_WIND_SPEED_UNIT = "wind_speed_unit"
100 CONF_VISIBILITY_UNIT = "visibility_unit"
101 CONF_PRECIPITATION_UNIT = "precipitation_unit"
102 CONF_WIND_GUST_SPEED_TEMPLATE = "wind_gust_speed_template"
103 CONF_CLOUD_COVERAGE_TEMPLATE = "cloud_coverage_template"
104 CONF_DEW_POINT_TEMPLATE = "dew_point_template"
105 CONF_APPARENT_TEMPERATURE_TEMPLATE = "apparent_temperature_template"
106 
107 WEATHER_SCHEMA = vol.Schema(
108  {
109  vol.Required(CONF_NAME): cv.template,
110  vol.Required(CONF_CONDITION_TEMPLATE): cv.template,
111  vol.Required(CONF_TEMPERATURE_TEMPLATE): cv.template,
112  vol.Required(CONF_HUMIDITY_TEMPLATE): cv.template,
113  vol.Optional(CONF_ATTRIBUTION_TEMPLATE): cv.template,
114  vol.Optional(CONF_PRESSURE_TEMPLATE): cv.template,
115  vol.Optional(CONF_WIND_SPEED_TEMPLATE): cv.template,
116  vol.Optional(CONF_WIND_BEARING_TEMPLATE): cv.template,
117  vol.Optional(CONF_OZONE_TEMPLATE): cv.template,
118  vol.Optional(CONF_VISIBILITY_TEMPLATE): cv.template,
119  vol.Optional(CONF_FORECAST_DAILY_TEMPLATE): cv.template,
120  vol.Optional(CONF_FORECAST_HOURLY_TEMPLATE): cv.template,
121  vol.Optional(CONF_FORECAST_TWICE_DAILY_TEMPLATE): cv.template,
122  vol.Optional(CONF_UNIQUE_ID): cv.string,
123  vol.Optional(CONF_TEMPERATURE_UNIT): vol.In(TemperatureConverter.VALID_UNITS),
124  vol.Optional(CONF_PRESSURE_UNIT): vol.In(PressureConverter.VALID_UNITS),
125  vol.Optional(CONF_WIND_SPEED_UNIT): vol.In(SpeedConverter.VALID_UNITS),
126  vol.Optional(CONF_VISIBILITY_UNIT): vol.In(DistanceConverter.VALID_UNITS),
127  vol.Optional(CONF_PRECIPITATION_UNIT): vol.In(DistanceConverter.VALID_UNITS),
128  vol.Optional(CONF_WIND_GUST_SPEED_TEMPLATE): cv.template,
129  vol.Optional(CONF_CLOUD_COVERAGE_TEMPLATE): cv.template,
130  vol.Optional(CONF_DEW_POINT_TEMPLATE): cv.template,
131  vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template,
132  }
133 )
134 
135 PLATFORM_SCHEMA = WEATHER_PLATFORM_SCHEMA.extend(WEATHER_SCHEMA.schema)
136 
137 
139  hass: HomeAssistant,
140  config: ConfigType,
141  async_add_entities: AddEntitiesCallback,
142  discovery_info: DiscoveryInfoType | None = None,
143 ) -> None:
144  """Set up the Template weather."""
145  if discovery_info and "coordinator" in discovery_info:
147  TriggerWeatherEntity(hass, discovery_info["coordinator"], config)
148  for config in discovery_info["entities"]
149  )
150  return
151 
152  config = rewrite_common_legacy_to_modern_conf(hass, config)
153  unique_id = config.get(CONF_UNIQUE_ID)
154 
156  [
158  hass,
159  config,
160  unique_id,
161  )
162  ]
163  )
164 
165 
167  """Representation of a weather condition."""
168 
169  _attr_should_poll = False
170 
171  def __init__(
172  self,
173  hass: HomeAssistant,
174  config: ConfigType,
175  unique_id: str | None,
176  ) -> None:
177  """Initialize the Template weather."""
178  super().__init__(hass, config=config, unique_id=unique_id)
179 
180  name = self._attr_name_attr_name
181  self._condition_template_condition_template = config[CONF_CONDITION_TEMPLATE]
182  self._temperature_template_temperature_template = config[CONF_TEMPERATURE_TEMPLATE]
183  self._humidity_template_humidity_template = config[CONF_HUMIDITY_TEMPLATE]
184  self._attribution_template_attribution_template = config.get(CONF_ATTRIBUTION_TEMPLATE)
185  self._pressure_template_pressure_template = config.get(CONF_PRESSURE_TEMPLATE)
186  self._wind_speed_template_wind_speed_template = config.get(CONF_WIND_SPEED_TEMPLATE)
187  self._wind_bearing_template_wind_bearing_template = config.get(CONF_WIND_BEARING_TEMPLATE)
188  self._ozone_template_ozone_template = config.get(CONF_OZONE_TEMPLATE)
189  self._visibility_template_visibility_template = config.get(CONF_VISIBILITY_TEMPLATE)
190  self._forecast_daily_template_forecast_daily_template = config.get(CONF_FORECAST_DAILY_TEMPLATE)
191  self._forecast_hourly_template_forecast_hourly_template = config.get(CONF_FORECAST_HOURLY_TEMPLATE)
192  self._forecast_twice_daily_template_forecast_twice_daily_template = config.get(
193  CONF_FORECAST_TWICE_DAILY_TEMPLATE
194  )
195  self._wind_gust_speed_template_wind_gust_speed_template = config.get(CONF_WIND_GUST_SPEED_TEMPLATE)
196  self._cloud_coverage_template_cloud_coverage_template = config.get(CONF_CLOUD_COVERAGE_TEMPLATE)
197  self._dew_point_template_dew_point_template = config.get(CONF_DEW_POINT_TEMPLATE)
198  self._apparent_temperature_template_apparent_temperature_template = config.get(
199  CONF_APPARENT_TEMPERATURE_TEMPLATE
200  )
201 
202  self._attr_native_precipitation_unit_attr_native_precipitation_unit = config.get(CONF_PRECIPITATION_UNIT)
203  self._attr_native_pressure_unit_attr_native_pressure_unit = config.get(CONF_PRESSURE_UNIT)
204  self._attr_native_temperature_unit_attr_native_temperature_unit = config.get(CONF_TEMPERATURE_UNIT)
205  self._attr_native_visibility_unit_attr_native_visibility_unit = config.get(CONF_VISIBILITY_UNIT)
206  self._attr_native_wind_speed_unit_attr_native_wind_speed_unit = config.get(CONF_WIND_SPEED_UNIT)
207 
208  self.entity_identity_identity_identity_id = async_generate_entity_id(ENTITY_ID_FORMAT, name, hass=hass)
209 
210  self._condition_condition = None
211  self._temperature_temperature = None
212  self._humidity_humidity = None
213  self._attribution_attribution = None
214  self._pressure_pressure = None
215  self._wind_speed_wind_speed = None
216  self._wind_bearing_wind_bearing = None
217  self._ozone_ozone = None
218  self._visibility_visibility = None
219  self._wind_gust_speed_wind_gust_speed = None
220  self._cloud_coverage_cloud_coverage = None
221  self._dew_point_dew_point = None
222  self._apparent_temperature_apparent_temperature = None
223  self._forecast_daily: list[Forecast] = []
224  self._forecast_hourly: list[Forecast] = []
225  self._forecast_twice_daily: list[Forecast] = []
226 
227  self._attr_supported_features_attr_supported_features = 0
228  if self._forecast_daily_template_forecast_daily_template:
229  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_DAILY
230  if self._forecast_hourly_template_forecast_hourly_template:
231  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_HOURLY
232  if self._forecast_twice_daily_template_forecast_twice_daily_template:
233  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_TWICE_DAILY
234 
235  @property
236  def condition(self) -> str | None:
237  """Return the current condition."""
238  return self._condition_condition
239 
240  @property
241  def native_temperature(self) -> float | None:
242  """Return the temperature."""
243  return self._temperature_temperature
244 
245  @property
246  def humidity(self) -> float | None:
247  """Return the humidity."""
248  return self._humidity_humidity
249 
250  @property
251  def native_wind_speed(self) -> float | None:
252  """Return the wind speed."""
253  return self._wind_speed_wind_speed
254 
255  @property
256  def wind_bearing(self) -> float | str | None:
257  """Return the wind bearing."""
258  return self._wind_bearing_wind_bearing
259 
260  @property
261  def ozone(self) -> float | None:
262  """Return the ozone level."""
263  return self._ozone_ozone
264 
265  @property
266  def native_visibility(self) -> float | None:
267  """Return the visibility."""
268  return self._visibility_visibility
269 
270  @property
271  def native_pressure(self) -> float | None:
272  """Return the air pressure."""
273  return self._pressure_pressure
274 
275  @property
276  def native_wind_gust_speed(self) -> float | None:
277  """Return the wind gust speed."""
278  return self._wind_gust_speed_wind_gust_speed
279 
280  @property
281  def cloud_coverage(self) -> float | None:
282  """Return the cloud coverage."""
283  return self._cloud_coverage_cloud_coverage
284 
285  @property
286  def native_dew_point(self) -> float | None:
287  """Return the dew point."""
288  return self._dew_point_dew_point
289 
290  @property
291  def native_apparent_temperature(self) -> float | None:
292  """Return the apparent temperature."""
293  return self._apparent_temperature_apparent_temperature
294 
295  async def async_forecast_daily(self) -> list[Forecast]:
296  """Return the daily forecast in native units."""
297  return self._forecast_daily
298 
299  async def async_forecast_hourly(self) -> list[Forecast]:
300  """Return the daily forecast in native units."""
301  return self._forecast_hourly
302 
303  async def async_forecast_twice_daily(self) -> list[Forecast]:
304  """Return the daily forecast in native units."""
305  return self._forecast_twice_daily
306 
307  @property
308  def attribution(self) -> str | None:
309  """Return the attribution."""
310  if self._attribution_attribution is None:
311  return "Powered by Home Assistant"
312  return self._attribution_attribution
313 
314  @callback
315  def _async_setup_templates(self) -> None:
316  """Set up templates."""
317 
318  if self._condition_template_condition_template:
319  self.add_template_attributeadd_template_attribute(
320  "_condition",
321  self._condition_template_condition_template,
322  lambda condition: condition if condition in CONDITION_CLASSES else None,
323  )
324  if self._temperature_template_temperature_template:
325  self.add_template_attributeadd_template_attribute(
326  "_temperature",
327  self._temperature_template_temperature_template,
328  )
329  if self._humidity_template_humidity_template:
330  self.add_template_attributeadd_template_attribute(
331  "_humidity",
332  self._humidity_template_humidity_template,
333  )
334  if self._attribution_template_attribution_template:
335  self.add_template_attributeadd_template_attribute(
336  "_attribution",
337  self._attribution_template_attribution_template,
338  )
339  if self._pressure_template_pressure_template:
340  self.add_template_attributeadd_template_attribute(
341  "_pressure",
342  self._pressure_template_pressure_template,
343  )
344  if self._wind_speed_template_wind_speed_template:
345  self.add_template_attributeadd_template_attribute(
346  "_wind_speed",
347  self._wind_speed_template_wind_speed_template,
348  )
349  if self._wind_bearing_template_wind_bearing_template:
350  self.add_template_attributeadd_template_attribute(
351  "_wind_bearing",
352  self._wind_bearing_template_wind_bearing_template,
353  )
354  if self._ozone_template_ozone_template:
355  self.add_template_attributeadd_template_attribute(
356  "_ozone",
357  self._ozone_template_ozone_template,
358  )
359  if self._visibility_template_visibility_template:
360  self.add_template_attributeadd_template_attribute(
361  "_visibility",
362  self._visibility_template_visibility_template,
363  )
364  if self._wind_gust_speed_template_wind_gust_speed_template:
365  self.add_template_attributeadd_template_attribute(
366  "_wind_gust_speed",
367  self._wind_gust_speed_template_wind_gust_speed_template,
368  )
369  if self._cloud_coverage_template_cloud_coverage_template:
370  self.add_template_attributeadd_template_attribute(
371  "_cloud_coverage",
372  self._cloud_coverage_template_cloud_coverage_template,
373  )
374  if self._dew_point_template_dew_point_template:
375  self.add_template_attributeadd_template_attribute(
376  "_dew_point",
377  self._dew_point_template_dew_point_template,
378  )
379  if self._apparent_temperature_template_apparent_temperature_template:
380  self.add_template_attributeadd_template_attribute(
381  "_apparent_temperature",
382  self._apparent_temperature_template_apparent_temperature_template,
383  )
384 
385  if self._forecast_daily_template_forecast_daily_template:
386  self.add_template_attributeadd_template_attribute(
387  "_forecast_daily",
388  self._forecast_daily_template_forecast_daily_template,
389  on_update=partial(self._update_forecast_update_forecast, "daily"),
390  validator=partial(self._validate_forecast_validate_forecast, "daily"),
391  )
392  if self._forecast_hourly_template_forecast_hourly_template:
393  self.add_template_attributeadd_template_attribute(
394  "_forecast_hourly",
395  self._forecast_hourly_template_forecast_hourly_template,
396  on_update=partial(self._update_forecast_update_forecast, "hourly"),
397  validator=partial(self._validate_forecast_validate_forecast, "hourly"),
398  )
399  if self._forecast_twice_daily_template_forecast_twice_daily_template:
400  self.add_template_attributeadd_template_attribute(
401  "_forecast_twice_daily",
402  self._forecast_twice_daily_template_forecast_twice_daily_template,
403  on_update=partial(self._update_forecast_update_forecast, "twice_daily"),
404  validator=partial(self._validate_forecast_validate_forecast, "twice_daily"),
405  )
406 
407  super()._async_setup_templates()
408 
409  @callback
411  self,
412  forecast_type: Literal["daily", "hourly", "twice_daily"],
413  result: list[Forecast] | TemplateError,
414  ) -> None:
415  """Save template result and trigger forecast listener."""
416  attr_result = None if isinstance(result, TemplateError) else result
417  setattr(self, f"_forecast_{forecast_type}", attr_result)
418  self.hasshass.async_create_task(
419  self.async_update_listenersasync_update_listeners([forecast_type]), eager_start=True
420  )
421 
422  @callback
424  self,
425  forecast_type: Literal["daily", "hourly", "twice_daily"],
426  result: Any,
427  ) -> list[Forecast] | None:
428  """Validate the forecasts."""
429  if result is None:
430  return None
431 
432  if not isinstance(result, list):
433  raise vol.Invalid(
434  "Forecasts is not a list, see Weather documentation https://www.home-assistant.io/integrations/weather/"
435  )
436  for forecast in result:
437  if not isinstance(forecast, dict):
438  raise vol.Invalid(
439  "Forecast in list is not a dict, see Weather documentation https://www.home-assistant.io/integrations/weather/"
440  )
441  diff_result = set().union(forecast.keys()).difference(CHECK_FORECAST_KEYS)
442  if diff_result:
443  raise vol.Invalid(
444  f"Only valid keys in Forecast are allowed, unallowed keys: ({diff_result}), "
445  "see Weather documentation https://www.home-assistant.io/integrations/weather/"
446  )
447  if forecast_type == "twice_daily" and "is_daytime" not in forecast:
448  raise vol.Invalid(
449  "`is_daytime` is missing in twice_daily forecast, see Weather documentation https://www.home-assistant.io/integrations/weather/"
450  )
451  if "datetime" not in forecast:
452  raise vol.Invalid(
453  "`datetime` is required in forecasts, see Weather documentation https://www.home-assistant.io/integrations/weather/"
454  )
455  continue
456  return result
457 
458 
459 @dataclass(kw_only=True)
461  """Object to hold extra stored data."""
462 
463  last_apparent_temperature: float | None
464  last_cloud_coverage: int | None
465  last_dew_point: float | None
466  last_humidity: float | None
467  last_ozone: float | None
468  last_pressure: float | None
469  last_temperature: float | None
470  last_visibility: float | None
471  last_wind_bearing: float | str | None
472  last_wind_gust_speed: float | None
473  last_wind_speed: float | None
474 
475  def as_dict(self) -> dict[str, Any]:
476  """Return a dict representation of the event data."""
477  return asdict(self)
478 
479  @classmethod
480  def from_dict(cls, restored: dict[str, Any]) -> Self | None:
481  """Initialize a stored event state from a dict."""
482  try:
483  return cls(
484  last_apparent_temperature=restored["last_apparent_temperature"],
485  last_cloud_coverage=restored["last_cloud_coverage"],
486  last_dew_point=restored["last_dew_point"],
487  last_humidity=restored["last_humidity"],
488  last_ozone=restored["last_ozone"],
489  last_pressure=restored["last_pressure"],
490  last_temperature=restored["last_temperature"],
491  last_visibility=restored["last_visibility"],
492  last_wind_bearing=restored["last_wind_bearing"],
493  last_wind_gust_speed=restored["last_wind_gust_speed"],
494  last_wind_speed=restored["last_wind_speed"],
495  )
496  except KeyError:
497  return None
498 
499 
501  """Sensor entity based on trigger data."""
502 
503  domain = WEATHER_DOMAIN
504  extra_template_keys = (
505  CONF_CONDITION_TEMPLATE,
506  CONF_TEMPERATURE_TEMPLATE,
507  CONF_HUMIDITY_TEMPLATE,
508  )
509 
510  def __init__(
511  self,
512  hass: HomeAssistant,
513  coordinator: TriggerUpdateCoordinator,
514  config: ConfigType,
515  ) -> None:
516  """Initialize."""
517  super().__init__(hass, coordinator, config)
518  self._attr_native_precipitation_unit_attr_native_precipitation_unit = config.get(CONF_PRECIPITATION_UNIT)
519  self._attr_native_pressure_unit_attr_native_pressure_unit = config.get(CONF_PRESSURE_UNIT)
520  self._attr_native_temperature_unit_attr_native_temperature_unit = config.get(CONF_TEMPERATURE_UNIT)
521  self._attr_native_visibility_unit_attr_native_visibility_unit = config.get(CONF_VISIBILITY_UNIT)
522  self._attr_native_wind_speed_unit_attr_native_wind_speed_unit = config.get(CONF_WIND_SPEED_UNIT)
523 
524  self._attr_supported_features_attr_supported_features = 0
525  if config.get(CONF_FORECAST_DAILY_TEMPLATE):
526  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_DAILY
527  if config.get(CONF_FORECAST_HOURLY_TEMPLATE):
528  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_HOURLY
529  if config.get(CONF_FORECAST_TWICE_DAILY_TEMPLATE):
530  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_TWICE_DAILY
531 
532  for key in (
533  CONF_APPARENT_TEMPERATURE_TEMPLATE,
534  CONF_CLOUD_COVERAGE_TEMPLATE,
535  CONF_DEW_POINT_TEMPLATE,
536  CONF_FORECAST_DAILY_TEMPLATE,
537  CONF_FORECAST_HOURLY_TEMPLATE,
538  CONF_FORECAST_TWICE_DAILY_TEMPLATE,
539  CONF_OZONE_TEMPLATE,
540  CONF_PRESSURE_TEMPLATE,
541  CONF_VISIBILITY_TEMPLATE,
542  CONF_WIND_BEARING_TEMPLATE,
543  CONF_WIND_GUST_SPEED_TEMPLATE,
544  CONF_WIND_SPEED_TEMPLATE,
545  ):
546  if isinstance(config.get(key), template.Template):
547  self._to_render_simple.append(key)
548  self._parse_result.add(key)
549 
550  async def async_added_to_hass(self) -> None:
551  """Restore last state."""
552  await super().async_added_to_hass()
553  if (
554  (state := await self.async_get_last_stateasync_get_last_state())
555  and state.state is not None
556  and state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
557  and (weather_data := await self.async_get_last_weather_dataasync_get_last_weather_data())
558  ):
559  self._rendered[CONF_APPARENT_TEMPERATURE_TEMPLATE] = (
560  weather_data.last_apparent_temperature
561  )
562  self._rendered[CONF_CLOUD_COVERAGE_TEMPLATE] = (
563  weather_data.last_cloud_coverage
564  )
565  self._rendered[CONF_CONDITION_TEMPLATE] = state.state
566  self._rendered[CONF_DEW_POINT_TEMPLATE] = weather_data.last_dew_point
567  self._rendered[CONF_HUMIDITY_TEMPLATE] = weather_data.last_humidity
568  self._rendered[CONF_OZONE_TEMPLATE] = weather_data.last_ozone
569  self._rendered[CONF_PRESSURE_TEMPLATE] = weather_data.last_pressure
570  self._rendered[CONF_TEMPERATURE_TEMPLATE] = weather_data.last_temperature
571  self._rendered[CONF_VISIBILITY_TEMPLATE] = weather_data.last_visibility
572  self._rendered[CONF_WIND_BEARING_TEMPLATE] = weather_data.last_wind_bearing
573  self._rendered[CONF_WIND_GUST_SPEED_TEMPLATE] = (
574  weather_data.last_wind_gust_speed
575  )
576  self._rendered[CONF_WIND_SPEED_TEMPLATE] = weather_data.last_wind_speed
577 
578  @property
579  def condition(self) -> str | None:
580  """Return the current condition."""
581  return self._rendered.get(CONF_CONDITION_TEMPLATE)
582 
583  @property
584  def native_temperature(self) -> float | None:
585  """Return the temperature."""
586  return vol.Any(vol.Coerce(float), None)(
587  self._rendered.get(CONF_TEMPERATURE_TEMPLATE)
588  )
589 
590  @property
591  def humidity(self) -> float | None:
592  """Return the humidity."""
593  return vol.Any(vol.Coerce(float), None)(
594  self._rendered.get(CONF_HUMIDITY_TEMPLATE)
595  )
596 
597  @property
598  def native_wind_speed(self) -> float | None:
599  """Return the wind speed."""
600  return vol.Any(vol.Coerce(float), None)(
601  self._rendered.get(CONF_WIND_SPEED_TEMPLATE)
602  )
603 
604  @property
605  def wind_bearing(self) -> float | str | None:
606  """Return the wind bearing."""
607  return vol.Any(vol.Coerce(float), vol.Coerce(str), None)(
608  self._rendered.get(CONF_WIND_BEARING_TEMPLATE)
609  )
610 
611  @property
612  def ozone(self) -> float | None:
613  """Return the ozone level."""
614  return vol.Any(vol.Coerce(float), None)(
615  self._rendered.get(CONF_OZONE_TEMPLATE),
616  )
617 
618  @property
619  def native_visibility(self) -> float | None:
620  """Return the visibility."""
621  return vol.Any(vol.Coerce(float), None)(
622  self._rendered.get(CONF_VISIBILITY_TEMPLATE)
623  )
624 
625  @property
626  def native_pressure(self) -> float | None:
627  """Return the air pressure."""
628  return vol.Any(vol.Coerce(float), None)(
629  self._rendered.get(CONF_PRESSURE_TEMPLATE)
630  )
631 
632  @property
633  def native_wind_gust_speed(self) -> float | None:
634  """Return the wind gust speed."""
635  return vol.Any(vol.Coerce(float), None)(
636  self._rendered.get(CONF_WIND_GUST_SPEED_TEMPLATE)
637  )
638 
639  @property
640  def cloud_coverage(self) -> float | None:
641  """Return the cloud coverage."""
642  return vol.Any(vol.Coerce(float), None)(
643  self._rendered.get(CONF_CLOUD_COVERAGE_TEMPLATE)
644  )
645 
646  @property
647  def native_dew_point(self) -> float | None:
648  """Return the dew point."""
649  return vol.Any(vol.Coerce(float), None)(
650  self._rendered.get(CONF_DEW_POINT_TEMPLATE)
651  )
652 
653  @property
654  def native_apparent_temperature(self) -> float | None:
655  """Return the apparent temperature."""
656  return vol.Any(vol.Coerce(float), None)(
657  self._rendered.get(CONF_APPARENT_TEMPERATURE_TEMPLATE)
658  )
659 
660  async def async_forecast_daily(self) -> list[Forecast]:
661  """Return the daily forecast in native units."""
662  return vol.Any(vol.Coerce(list), None)(
663  self._rendered.get(CONF_FORECAST_DAILY_TEMPLATE)
664  )
665 
666  async def async_forecast_hourly(self) -> list[Forecast]:
667  """Return the daily forecast in native units."""
668  return vol.Any(vol.Coerce(list), None)(
669  self._rendered.get(CONF_FORECAST_HOURLY_TEMPLATE)
670  )
671 
672  async def async_forecast_twice_daily(self) -> list[Forecast]:
673  """Return the daily forecast in native units."""
674  return vol.Any(vol.Coerce(list), None)(
675  self._rendered.get(CONF_FORECAST_TWICE_DAILY_TEMPLATE)
676  )
677 
678  @property
679  def extra_restore_state_data(self) -> WeatherExtraStoredData:
680  """Return weather specific state data to be restored."""
681  return WeatherExtraStoredData(
682  last_apparent_temperature=self._rendered.get(
683  CONF_APPARENT_TEMPERATURE_TEMPLATE
684  ),
685  last_cloud_coverage=self._rendered.get(CONF_CLOUD_COVERAGE_TEMPLATE),
686  last_dew_point=self._rendered.get(CONF_DEW_POINT_TEMPLATE),
687  last_humidity=self._rendered.get(CONF_HUMIDITY_TEMPLATE),
688  last_ozone=self._rendered.get(CONF_OZONE_TEMPLATE),
689  last_pressure=self._rendered.get(CONF_PRESSURE_TEMPLATE),
690  last_temperature=self._rendered.get(CONF_TEMPERATURE_TEMPLATE),
691  last_visibility=self._rendered.get(CONF_VISIBILITY_TEMPLATE),
692  last_wind_bearing=self._rendered.get(CONF_WIND_BEARING_TEMPLATE),
693  last_wind_gust_speed=self._rendered.get(CONF_WIND_GUST_SPEED_TEMPLATE),
694  last_wind_speed=self._rendered.get(CONF_WIND_SPEED_TEMPLATE),
695  )
696 
697  async def async_get_last_weather_data(self) -> WeatherExtraStoredData | None:
698  """Restore weather specific state data."""
699  if (restored_last_extra_data := await self.async_get_last_extra_dataasync_get_last_extra_data()) is None:
700  return None
701  return WeatherExtraStoredData.from_dict(restored_last_extra_data.as_dict())
None add_template_attribute(self, str attribute, Template template, Callable[[Any], Any]|None validator=None, Callable[[Any], None]|None on_update=None, bool none_on_template_error=False)
WeatherExtraStoredData|None async_get_last_weather_data(self)
Definition: weather.py:697
None __init__(self, HomeAssistant hass, TriggerUpdateCoordinator coordinator, ConfigType config)
Definition: weather.py:515
Self|None from_dict(cls, dict[str, Any] restored)
Definition: weather.py:480
None _update_forecast(self, Literal["daily", "hourly", "twice_daily"] forecast_type, list[Forecast]|TemplateError result)
Definition: weather.py:414
None __init__(self, HomeAssistant hass, ConfigType config, str|None unique_id)
Definition: weather.py:176
list[Forecast]|None _validate_forecast(self, Literal["daily", "hourly", "twice_daily"] forecast_type, Any result)
Definition: weather.py:427
None async_update_listeners(self, Iterable[Literal["daily", "hourly", "twice_daily"]]|None forecast_types)
Definition: __init__.py:961
ExtraStoredData|None async_get_last_extra_data(self)
bool add(self, _T matcher)
Definition: match.py:185
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
dict[str, Any] rewrite_common_legacy_to_modern_conf(HomeAssistant hass, dict[str, Any] entity_cfg, dict[str, str]|None extra_legacy_fields=None)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: weather.py:143
str async_generate_entity_id(str entity_id_format, str|None name, Iterable[str]|None current_ids=None, HomeAssistant|None hass=None)
Definition: entity.py:119