Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for Meteo-France weather data."""
2 
3 from datetime import timedelta
4 import logging
5 
6 from meteofrance_api.client import MeteoFranceClient
7 from meteofrance_api.helpers import is_valid_warning_department
8 from meteofrance_api.model import CurrentPhenomenons, Forecast, Rain
9 import voluptuous as vol
10 
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
13 from homeassistant.core import HomeAssistant
14 from homeassistant.exceptions import ConfigEntryNotReady
16 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
17 
18 from .const import (
19  CONF_CITY,
20  COORDINATOR_ALERT,
21  COORDINATOR_FORECAST,
22  COORDINATOR_RAIN,
23  DOMAIN,
24  PLATFORMS,
25  UNDO_UPDATE_LISTENER,
26 )
27 
28 _LOGGER = logging.getLogger(__name__)
29 
30 SCAN_INTERVAL_RAIN = timedelta(minutes=5)
31 SCAN_INTERVAL = timedelta(minutes=15)
32 
33 
34 CITY_SCHEMA = vol.Schema({vol.Required(CONF_CITY): cv.string})
35 
36 
37 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
38  """Set up an Meteo-France account from a config entry."""
39  hass.data.setdefault(DOMAIN, {})
40 
41  client = MeteoFranceClient()
42  latitude = entry.data[CONF_LATITUDE]
43  longitude = entry.data[CONF_LONGITUDE]
44 
45  async def _async_update_data_forecast_forecast() -> Forecast:
46  """Fetch data from API endpoint."""
47  return await hass.async_add_executor_job(
48  client.get_forecast, latitude, longitude
49  )
50 
51  async def _async_update_data_rain() -> Rain:
52  """Fetch data from API endpoint."""
53  return await hass.async_add_executor_job(client.get_rain, latitude, longitude)
54 
55  async def _async_update_data_alert() -> CurrentPhenomenons:
56  """Fetch data from API endpoint."""
57  assert isinstance(department, str)
58  return await hass.async_add_executor_job(
59  client.get_warning_current_phenomenoms, department, 0, True
60  )
61 
62  coordinator_forecast = DataUpdateCoordinator(
63  hass,
64  _LOGGER,
65  name=f"Météo-France forecast for city {entry.title}",
66  update_method=_async_update_data_forecast_forecast,
67  update_interval=SCAN_INTERVAL,
68  )
69  coordinator_rain = None
70  coordinator_alert = None
71 
72  # Fetch initial data so we have data when entities subscribe
73  await coordinator_forecast.async_refresh()
74 
75  if not coordinator_forecast.last_update_success:
76  raise ConfigEntryNotReady
77 
78  # Check rain forecast.
79  coordinator_rain = DataUpdateCoordinator(
80  hass,
81  _LOGGER,
82  name=f"Météo-France rain for city {entry.title}",
83  update_method=_async_update_data_rain,
84  update_interval=SCAN_INTERVAL_RAIN,
85  )
86  await coordinator_rain.async_config_entry_first_refresh()
87 
88  department = coordinator_forecast.data.position.get("dept")
89  _LOGGER.debug(
90  "Department corresponding to %s is %s",
91  entry.title,
92  department,
93  )
94  if department is not None and is_valid_warning_department(department):
95  if not hass.data[DOMAIN].get(department):
96  coordinator_alert = DataUpdateCoordinator(
97  hass,
98  _LOGGER,
99  name=f"Météo-France alert for department {department}",
100  update_method=_async_update_data_alert,
101  update_interval=SCAN_INTERVAL,
102  )
103 
104  await coordinator_alert.async_refresh()
105 
106  if coordinator_alert.last_update_success:
107  hass.data[DOMAIN][department] = True
108  else:
109  _LOGGER.warning(
110  (
111  "Weather alert for department %s won't be added with city %s, as it"
112  " has already been added within another city"
113  ),
114  department,
115  entry.title,
116  )
117  else:
118  _LOGGER.warning(
119  (
120  "Weather alert not available: The city %s is not in metropolitan France"
121  " or Andorre"
122  ),
123  entry.title,
124  )
125 
126  undo_listener = entry.add_update_listener(_async_update_listener)
127 
128  hass.data[DOMAIN][entry.entry_id] = {
129  UNDO_UPDATE_LISTENER: undo_listener,
130  COORDINATOR_FORECAST: coordinator_forecast,
131  COORDINATOR_RAIN: coordinator_rain,
132  }
133  if coordinator_alert and coordinator_alert.last_update_success:
134  hass.data[DOMAIN][entry.entry_id][COORDINATOR_ALERT] = coordinator_alert
135 
136  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
137 
138  return True
139 
140 
141 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
142  """Unload a config entry."""
143  if hass.data[DOMAIN][entry.entry_id][COORDINATOR_ALERT]:
144  department = hass.data[DOMAIN][entry.entry_id][
145  COORDINATOR_FORECAST
146  ].data.position.get("dept")
147  hass.data[DOMAIN][department] = False
148  _LOGGER.debug(
149  (
150  "Weather alert for depatment %s unloaded and released. It can be added"
151  " now by another city"
152  ),
153  department,
154  )
155 
156  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
157  if unload_ok:
158  hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
159  hass.data[DOMAIN].pop(entry.entry_id)
160  if not hass.data[DOMAIN]:
161  hass.data.pop(DOMAIN)
162 
163  return unload_ok
164 
165 
166 async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
167  """Handle options update."""
168  await hass.config_entries.async_reload(entry.entry_id)
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:37
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:141
None _async_update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:166