Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for World Air Quality Index (WAQI) integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from aiowaqi import (
9  WAQIAirQuality,
10  WAQIAuthenticationError,
11  WAQIClient,
12  WAQIConnectionError,
13 )
14 import voluptuous as vol
15 
16 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
17 from homeassistant.const import (
18  CONF_API_KEY,
19  CONF_LATITUDE,
20  CONF_LOCATION,
21  CONF_LONGITUDE,
22  CONF_METHOD,
23 )
24 from homeassistant.helpers.aiohttp_client import async_get_clientsession
26  LocationSelector,
27  SelectSelector,
28  SelectSelectorConfig,
29 )
30 
31 from .const import CONF_STATION_NUMBER, DOMAIN
32 
33 _LOGGER = logging.getLogger(__name__)
34 
35 CONF_MAP = "map"
36 
37 
39  client: WAQIClient, station_number: int
40 ) -> tuple[WAQIAirQuality | None, dict[str, str]]:
41  """Get measuring station by station number."""
42  errors: dict[str, str] = {}
43  measuring_station: WAQIAirQuality | None = None
44  try:
45  measuring_station = await client.get_by_station_number(station_number)
46  except WAQIConnectionError:
47  errors["base"] = "cannot_connect"
48  except Exception:
49  _LOGGER.exception("Unexpected exception")
50  errors["base"] = "unknown"
51  return measuring_station, errors
52 
53 
54 class WAQIConfigFlow(ConfigFlow, domain=DOMAIN):
55  """Handle a config flow for World Air Quality Index (WAQI)."""
56 
57  VERSION = 1
58 
59  def __init__(self) -> None:
60  """Initialize config flow."""
61  self.datadata: dict[str, Any] = {}
62 
63  async def async_step_user(
64  self, user_input: dict[str, Any] | None = None
65  ) -> ConfigFlowResult:
66  """Handle the initial step."""
67  errors: dict[str, str] = {}
68  if user_input is not None:
69  async with WAQIClient(
70  session=async_get_clientsession(self.hass)
71  ) as waqi_client:
72  waqi_client.authenticate(user_input[CONF_API_KEY])
73  try:
74  await waqi_client.get_by_ip()
75  except WAQIAuthenticationError:
76  errors["base"] = "invalid_auth"
77  except WAQIConnectionError:
78  errors["base"] = "cannot_connect"
79  except Exception:
80  _LOGGER.exception("Unexpected exception")
81  errors["base"] = "unknown"
82  else:
83  self.datadata = user_input
84  if user_input[CONF_METHOD] == CONF_MAP:
85  return await self.async_step_mapasync_step_map()
86  return await self.async_step_station_numberasync_step_station_number()
87 
88  return self.async_show_formasync_show_formasync_show_form(
89  step_id="user",
90  data_schema=vol.Schema(
91  {
92  vol.Required(CONF_API_KEY): str,
93  vol.Required(CONF_METHOD): SelectSelector(
95  options=[CONF_MAP, CONF_STATION_NUMBER],
96  translation_key="method",
97  )
98  ),
99  }
100  ),
101  errors=errors,
102  )
103 
104  async def async_step_map(
105  self, user_input: dict[str, Any] | None = None
106  ) -> ConfigFlowResult:
107  """Add measuring station via map."""
108  errors: dict[str, str] = {}
109  if user_input is not None:
110  async with WAQIClient(
111  session=async_get_clientsession(self.hass)
112  ) as waqi_client:
113  waqi_client.authenticate(self.datadata[CONF_API_KEY])
114  try:
115  measuring_station = await waqi_client.get_by_coordinates(
116  user_input[CONF_LOCATION][CONF_LATITUDE],
117  user_input[CONF_LOCATION][CONF_LONGITUDE],
118  )
119  except WAQIConnectionError:
120  errors["base"] = "cannot_connect"
121  except Exception:
122  _LOGGER.exception("Unexpected exception")
123  errors["base"] = "unknown"
124  else:
125  return await self._async_create_entry_async_create_entry(measuring_station)
126  return self.async_show_formasync_show_formasync_show_form(
127  step_id=CONF_MAP,
128  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
129  vol.Schema(
130  {
131  vol.Required(
132  CONF_LOCATION,
133  ): LocationSelector(),
134  }
135  ),
136  {
137  CONF_LOCATION: {
138  CONF_LATITUDE: self.hass.config.latitude,
139  CONF_LONGITUDE: self.hass.config.longitude,
140  }
141  },
142  ),
143  errors=errors,
144  )
145 
147  self, user_input: dict[str, Any] | None = None
148  ) -> ConfigFlowResult:
149  """Add measuring station via station number."""
150  errors: dict[str, str] = {}
151  if user_input is not None:
152  async with WAQIClient(
153  session=async_get_clientsession(self.hass)
154  ) as waqi_client:
155  waqi_client.authenticate(self.datadata[CONF_API_KEY])
156  station_number = user_input[CONF_STATION_NUMBER]
157  measuring_station, errors = await get_by_station_number(
158  waqi_client, abs(station_number)
159  )
160  if not measuring_station:
161  measuring_station, _ = await get_by_station_number(
162  waqi_client,
163  abs(station_number) - station_number - station_number,
164  )
165  if measuring_station:
166  return await self._async_create_entry_async_create_entry(measuring_station)
167  return self.async_show_formasync_show_formasync_show_form(
168  step_id=CONF_STATION_NUMBER,
169  data_schema=vol.Schema(
170  {
171  vol.Required(
172  CONF_STATION_NUMBER,
173  ): int,
174  }
175  ),
176  errors=errors,
177  )
178 
180  self, measuring_station: WAQIAirQuality
181  ) -> ConfigFlowResult:
182  await self.async_set_unique_idasync_set_unique_id(str(measuring_station.station_id))
183  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
184  return self.async_create_entryasync_create_entryasync_create_entry(
185  title=measuring_station.city.name,
186  data={
187  CONF_API_KEY: self.datadata[CONF_API_KEY],
188  CONF_STATION_NUMBER: measuring_station.station_id,
189  },
190  )
ConfigFlowResult async_step_station_number(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:148
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:65
ConfigFlowResult async_step_map(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:106
ConfigFlowResult _async_create_entry(self, WAQIAirQuality measuring_station)
Definition: config_flow.py:181
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
ConfigEntry|None async_set_unique_id(self, str|None unique_id=None, *bool raise_on_progress=True)
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
str
vol.Schema add_suggested_values_to_schema(self, vol.Schema data_schema, Mapping[str, Any]|None suggested_values)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
tuple[WAQIAirQuality|None, dict[str, str]] get_by_station_number(WAQIClient client, int station_number)
Definition: config_flow.py:40
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)