Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Met Office integration."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 import logging
7 import re
8 from typing import Any
9 
10 import datapoint
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import (
14  CONF_API_KEY,
15  CONF_LATITUDE,
16  CONF_LONGITUDE,
17  CONF_NAME,
18  Platform,
19 )
20 from homeassistant.core import HomeAssistant, callback
21 from homeassistant.exceptions import ConfigEntryNotReady
22 from homeassistant.helpers import device_registry as dr, entity_registry as er
23 from homeassistant.helpers.device_registry import DeviceInfo
24 from homeassistant.helpers.update_coordinator import TimestampDataUpdateCoordinator
25 
26 from .const import (
27  DEFAULT_SCAN_INTERVAL,
28  DOMAIN,
29  METOFFICE_COORDINATES,
30  METOFFICE_DAILY_COORDINATOR,
31  METOFFICE_HOURLY_COORDINATOR,
32  METOFFICE_NAME,
33  MODE_3HOURLY,
34  MODE_DAILY,
35 )
36 from .data import MetOfficeData
37 from .helpers import fetch_data, fetch_site
38 
39 _LOGGER = logging.getLogger(__name__)
40 
41 PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
42 
43 
44 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
45  """Set up a Met Office entry."""
46 
47  latitude = entry.data[CONF_LATITUDE]
48  longitude = entry.data[CONF_LONGITUDE]
49  api_key = entry.data[CONF_API_KEY]
50  site_name = entry.data[CONF_NAME]
51 
52  coordinates = f"{latitude}_{longitude}"
53 
54  @callback
55  def update_unique_id(
56  entity_entry: er.RegistryEntry,
57  ) -> dict[str, Any] | None:
58  """Update unique ID of entity entry."""
59 
60  if entity_entry.domain != Platform.SENSOR:
61  return None
62 
63  name_to_key = {
64  "Station Name": "name",
65  "Weather": "weather",
66  "Temperature": "temperature",
67  "Feels Like Temperature": "feels_like_temperature",
68  "Wind Speed": "wind_speed",
69  "Wind Direction": "wind_direction",
70  "Wind Gust": "wind_gust",
71  "Visibility": "visibility",
72  "Visibility Distance": "visibility_distance",
73  "UV Index": "uv",
74  "Probability of Precipitation": "precipitation",
75  "Humidity": "humidity",
76  }
77 
78  match = re.search(f"(?P<name>.*)_{coordinates}.*", entity_entry.unique_id)
79 
80  if match is None:
81  return None
82 
83  if (name := match.group("name")) in name_to_key:
84  return {
85  "new_unique_id": entity_entry.unique_id.replace(name, name_to_key[name])
86  }
87  return None
88 
89  await er.async_migrate_entries(hass, entry.entry_id, update_unique_id)
90 
91  connection = datapoint.connection(api_key=api_key)
92 
93  site = await hass.async_add_executor_job(
94  fetch_site, connection, latitude, longitude
95  )
96  if site is None:
97  raise ConfigEntryNotReady
98 
99  async def async_update_3hourly() -> MetOfficeData:
100  return await hass.async_add_executor_job(
101  fetch_data, connection, site, MODE_3HOURLY
102  )
103 
104  async def async_update_daily() -> MetOfficeData:
105  return await hass.async_add_executor_job(
106  fetch_data, connection, site, MODE_DAILY
107  )
108 
109  metoffice_hourly_coordinator = TimestampDataUpdateCoordinator(
110  hass,
111  _LOGGER,
112  config_entry=entry,
113  name=f"MetOffice Hourly Coordinator for {site_name}",
114  update_method=async_update_3hourly,
115  update_interval=DEFAULT_SCAN_INTERVAL,
116  )
117 
118  metoffice_daily_coordinator = TimestampDataUpdateCoordinator(
119  hass,
120  _LOGGER,
121  config_entry=entry,
122  name=f"MetOffice Daily Coordinator for {site_name}",
123  update_method=async_update_daily,
124  update_interval=DEFAULT_SCAN_INTERVAL,
125  )
126 
127  metoffice_hass_data = hass.data.setdefault(DOMAIN, {})
128  metoffice_hass_data[entry.entry_id] = {
129  METOFFICE_HOURLY_COORDINATOR: metoffice_hourly_coordinator,
130  METOFFICE_DAILY_COORDINATOR: metoffice_daily_coordinator,
131  METOFFICE_NAME: site_name,
132  METOFFICE_COORDINATES: coordinates,
133  }
134 
135  # Fetch initial data so we have data when entities subscribe
136  await asyncio.gather(
137  metoffice_hourly_coordinator.async_config_entry_first_refresh(),
138  metoffice_daily_coordinator.async_config_entry_first_refresh(),
139  )
140 
141  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
142 
143  return True
144 
145 
146 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
147  """Unload a config entry."""
148  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
149  if unload_ok:
150  hass.data[DOMAIN].pop(entry.entry_id)
151  if not hass.data[DOMAIN]:
152  hass.data.pop(DOMAIN)
153  return unload_ok
154 
155 
156 def get_device_info(coordinates: str, name: str) -> DeviceInfo:
157  """Return device registry information."""
158  return DeviceInfo(
159  entry_type=dr.DeviceEntryType.SERVICE,
160  identifiers={(DOMAIN, coordinates)},
161  manufacturer="Met Office",
162  name=f"Met Office {name}",
163  )
dict[str, str]|None update_unique_id(er.RegistryEntry entity_entry, str unique_id)
Definition: __init__.py:168
DeviceInfo get_device_info(str coordinates, str name)
Definition: __init__.py:156
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:146
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:44