Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow to configure the OpenUV component."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 from dataclasses import dataclass
7 from typing import Any
8 
9 from pyopenuv import Client
10 from pyopenuv.errors import OpenUvError
11 import voluptuous as vol
12 
13 from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
14 from homeassistant.const import (
15  CONF_API_KEY,
16  CONF_ELEVATION,
17  CONF_LATITUDE,
18  CONF_LONGITUDE,
19 )
20 from homeassistant.core import callback
21 from homeassistant.helpers import aiohttp_client, config_validation as cv
23  SchemaFlowFormStep,
24  SchemaOptionsFlowHandler,
25 )
26 
27 from .const import (
28  CONF_FROM_WINDOW,
29  CONF_TO_WINDOW,
30  DEFAULT_FROM_WINDOW,
31  DEFAULT_TO_WINDOW,
32  DOMAIN,
33 )
34 
35 STEP_REAUTH_SCHEMA = vol.Schema(
36  {
37  vol.Required(CONF_API_KEY): str,
38  }
39 )
40 
41 OPTIONS_SCHEMA = vol.Schema(
42  {
43  vol.Optional(
44  CONF_FROM_WINDOW, description={"suggested_value": DEFAULT_FROM_WINDOW}
45  ): vol.Coerce(float),
46  vol.Optional(
47  CONF_TO_WINDOW, description={"suggested_value": DEFAULT_TO_WINDOW}
48  ): vol.Coerce(float),
49  }
50 )
51 
52 OPTIONS_FLOW = {
53  "init": SchemaFlowFormStep(OPTIONS_SCHEMA),
54 }
55 
56 
57 @dataclass
58 class OpenUvData:
59  """Define structured OpenUV data needed to create/re-auth an entry."""
60 
61  api_key: str
62  latitude: float
63  longitude: float
64  elevation: float
65 
66  @property
67  def unique_id(self) -> str:
68  """Return the unique for this data."""
69  return f"{self.latitude}, {self.longitude}"
70 
71 
72 class OpenUvFlowHandler(ConfigFlow, domain=DOMAIN):
73  """Handle an OpenUV config flow."""
74 
75  VERSION = 2
76 
77  def __init__(self) -> None:
78  """Initialize."""
79  self._reauth_data_reauth_data: Mapping[str, Any] = {}
80 
81  @property
82  def step_user_schema(self) -> vol.Schema:
83  """Return the config schema."""
84  return vol.Schema(
85  {
86  vol.Required(CONF_API_KEY): str,
87  vol.Inclusive(
88  CONF_LATITUDE, "coords", default=self.hass.config.latitude
89  ): cv.latitude,
90  vol.Inclusive(
91  CONF_LONGITUDE, "coords", default=self.hass.config.longitude
92  ): cv.longitude,
93  vol.Optional(
94  CONF_ELEVATION, default=self.hass.config.elevation
95  ): vol.Coerce(float),
96  }
97  )
98 
99  async def _async_verify(
100  self, data: OpenUvData, error_step_id: str, error_schema: vol.Schema
101  ) -> ConfigFlowResult:
102  """Verify the credentials and create/re-auth the entry."""
103  websession = aiohttp_client.async_get_clientsession(self.hass)
104  client = Client(data.api_key, 0, 0, session=websession)
105 
106  try:
107  await client.uv_index()
108  except OpenUvError:
109  return self.async_show_formasync_show_formasync_show_form(
110  step_id=error_step_id,
111  data_schema=error_schema,
112  errors={CONF_API_KEY: "invalid_api_key"},
113  description_placeholders={
114  CONF_LATITUDE: str(data.latitude),
115  CONF_LONGITUDE: str(data.longitude),
116  },
117  )
118 
119  entry_data = {
120  CONF_API_KEY: data.api_key,
121  CONF_LATITUDE: data.latitude,
122  CONF_LONGITUDE: data.longitude,
123  CONF_ELEVATION: data.elevation,
124  }
125 
126  if existing_entry := await self.async_set_unique_idasync_set_unique_id(data.unique_id):
127  self.hass.config_entries.async_update_entry(existing_entry, data=entry_data)
128  self.hass.async_create_task(
129  self.hass.config_entries.async_reload(existing_entry.entry_id)
130  )
131  return self.async_abortasync_abortasync_abort(reason="reauth_successful")
132  return self.async_create_entryasync_create_entryasync_create_entry(title=data.unique_id, data=entry_data)
133 
134  @staticmethod
135  @callback
136  def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler:
137  """Define the config flow to handle options."""
138  return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
139 
140  async def async_step_reauth(
141  self, entry_data: Mapping[str, Any]
142  ) -> ConfigFlowResult:
143  """Handle configuration by re-auth."""
144  self._reauth_data_reauth_data = entry_data
145  return await self.async_step_reauth_confirmasync_step_reauth_confirm()
146 
148  self, user_input: dict[str, Any] | None = None
149  ) -> ConfigFlowResult:
150  """Handle re-auth completion."""
151  if not user_input:
152  return self.async_show_formasync_show_formasync_show_form(
153  step_id="reauth_confirm",
154  data_schema=STEP_REAUTH_SCHEMA,
155  description_placeholders={
156  CONF_LATITUDE: self._reauth_data_reauth_data[CONF_LATITUDE],
157  CONF_LONGITUDE: self._reauth_data_reauth_data[CONF_LONGITUDE],
158  },
159  )
160 
161  data = OpenUvData(
162  user_input[CONF_API_KEY],
163  self._reauth_data_reauth_data[CONF_LATITUDE],
164  self._reauth_data_reauth_data[CONF_LONGITUDE],
165  self._reauth_data_reauth_data[CONF_ELEVATION],
166  )
167 
168  return await self._async_verify_async_verify(data, "reauth_confirm", STEP_REAUTH_SCHEMA)
169 
170  async def async_step_user(
171  self, user_input: dict[str, Any] | None = None
172  ) -> ConfigFlowResult:
173  """Handle the start of the config flow."""
174  if not user_input:
175  return self.async_show_formasync_show_formasync_show_form(
176  step_id="user", data_schema=self.step_user_schemastep_user_schema
177  )
178 
179  data = OpenUvData(
180  user_input[CONF_API_KEY],
181  user_input[CONF_LATITUDE],
182  user_input[CONF_LONGITUDE],
183  user_input[CONF_ELEVATION],
184  )
185 
186  await self.async_set_unique_idasync_set_unique_id(data.unique_id)
187  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
188 
189  return await self._async_verify_async_verify(data, "user", self.step_user_schemastep_user_schema)
ConfigFlowResult _async_verify(self, OpenUvData data, str error_step_id, vol.Schema error_schema)
Definition: config_flow.py:101
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:172
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
Definition: config_flow.py:142
SchemaOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
Definition: config_flow.py:136
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:149
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_abort(self, *str reason, Mapping[str, str]|None description_placeholders=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
_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)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)