Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for Met.no integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from datetime import timedelta
7 import logging
8 from random import randrange
9 from types import MappingProxyType
10 from typing import Any, Self
11 
12 import metno
13 
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.const import (
16  CONF_ELEVATION,
17  CONF_LATITUDE,
18  CONF_LONGITUDE,
19  EVENT_CORE_CONFIG_UPDATE,
20 )
21 from homeassistant.core import Event, HomeAssistant
22 from homeassistant.exceptions import HomeAssistantError
23 from homeassistant.helpers.aiohttp_client import async_get_clientsession
24 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
25 from homeassistant.util import dt as dt_util
26 
27 from .const import CONF_TRACK_HOME, DOMAIN
28 
29 # Dedicated Home Assistant endpoint - do not change!
30 URL = "https://aa015h6buqvih86i1.api.met.no/weatherapi/locationforecast/2.0/complete"
31 
32 _LOGGER = logging.getLogger(__name__)
33 
34 
36  """Unable to connect to the web site."""
37 
38 
40  """Keep data for Met.no weather entities."""
41 
42  def __init__(self, hass: HomeAssistant, config: MappingProxyType[str, Any]) -> None:
43  """Initialise the weather entity data."""
44  self.hasshass = hass
45  self._config_config = config
46  self._weather_data_weather_data: metno.MetWeatherData
47  self.current_weather_datacurrent_weather_data: dict = {}
48  self.daily_forecastdaily_forecast: list[dict] = []
49  self.hourly_forecasthourly_forecast: list[dict] = []
50  self._coordinates_coordinates: dict[str, str] | None = None
51 
52  def set_coordinates(self) -> bool:
53  """Weather data initialization - set the coordinates."""
54  if self._config_config.get(CONF_TRACK_HOME, False):
55  latitude = self.hasshass.config.latitude
56  longitude = self.hasshass.config.longitude
57  elevation = self.hasshass.config.elevation
58  else:
59  latitude = self._config_config[CONF_LATITUDE]
60  longitude = self._config_config[CONF_LONGITUDE]
61  elevation = self._config_config[CONF_ELEVATION]
62 
63  coordinates = {
64  "lat": str(latitude),
65  "lon": str(longitude),
66  "msl": str(elevation),
67  }
68  if coordinates == self._coordinates_coordinates:
69  return False
70  self._coordinates_coordinates = coordinates
71 
72  self._weather_data_weather_data = metno.MetWeatherData(
73  coordinates, async_get_clientsession(self.hasshass), api_url=URL
74  )
75  return True
76 
77  async def fetch_data(self) -> Self:
78  """Fetch data from API - (current weather and forecast)."""
79  resp = await self._weather_data_weather_data.fetching_data()
80  if not resp:
81  raise CannotConnect
82  self.current_weather_datacurrent_weather_data = self._weather_data_weather_data.get_current_weather()
83  time_zone = dt_util.get_default_time_zone()
84  self.daily_forecastdaily_forecast = self._weather_data_weather_data.get_forecast(time_zone, False, 0)
85  self.hourly_forecasthourly_forecast = self._weather_data_weather_data.get_forecast(time_zone, True)
86  return self
87 
88 
90  """Class to manage fetching Met data."""
91 
92  def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
93  """Initialize global Met data updater."""
94  self._unsub_track_home_unsub_track_home: Callable[[], None] | None = None
95  self.weatherweather = MetWeatherData(hass, config_entry.data)
96  self.weatherweather.set_coordinates()
97 
98  update_interval = timedelta(minutes=randrange(55, 65))
99 
100  super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
101 
102  async def _async_update_data(self) -> MetWeatherData:
103  """Fetch data from Met."""
104  try:
105  return await self.weatherweather.fetch_data()
106  except Exception as err:
107  raise UpdateFailed(f"Update failed: {err}") from err
108 
109  def track_home(self) -> None:
110  """Start tracking changes to HA home setting."""
111  if self._unsub_track_home_unsub_track_home:
112  return
113 
114  async def _async_update_weather_data(_event: Event | None = None) -> None:
115  """Update weather data."""
116  if self.weatherweather.set_coordinates():
117  await self.async_refreshasync_refresh()
118 
119  self._unsub_track_home_unsub_track_home = self.hasshasshass.bus.async_listen(
120  EVENT_CORE_CONFIG_UPDATE, _async_update_weather_data
121  )
122 
123  def untrack_home(self) -> None:
124  """Stop tracking changes to HA home setting."""
125  if self._unsub_track_home_unsub_track_home:
126  self._unsub_track_home_unsub_track_home()
127  self._unsub_track_home_unsub_track_home = None
None __init__(self, HomeAssistant hass, ConfigEntry config_entry)
Definition: coordinator.py:92
None __init__(self, HomeAssistant hass, MappingProxyType[str, Any] config)
Definition: coordinator.py:42
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
list[Forecast]|None get_forecast(ec_data, hourly)
Definition: weather.py:187
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)