Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Shark IQ integration."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Mapping
7 from typing import Any
8 
9 import aiohttp
10 from sharkiq import SharkIqAuthError, get_ayla_api
11 import voluptuous as vol
12 
13 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
14 from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME
15 from homeassistant.core import HomeAssistant
16 from homeassistant.exceptions import HomeAssistantError
17 from homeassistant.helpers import selector
18 from homeassistant.helpers.aiohttp_client import async_get_clientsession
19 
20 from .const import (
21  DOMAIN,
22  LOGGER,
23  SHARKIQ_REGION_DEFAULT,
24  SHARKIQ_REGION_EUROPE,
25  SHARKIQ_REGION_OPTIONS,
26 )
27 
28 SHARKIQ_SCHEMA = vol.Schema(
29  {
30  vol.Required(CONF_USERNAME): str,
31  vol.Required(CONF_PASSWORD): str,
32  vol.Required(
33  CONF_REGION, default=SHARKIQ_REGION_DEFAULT
34  ): selector.SelectSelector(
35  selector.SelectSelectorConfig(
36  options=SHARKIQ_REGION_OPTIONS, translation_key="region"
37  ),
38  ),
39  }
40 )
41 
42 
43 async def _validate_input(
44  hass: HomeAssistant, data: Mapping[str, Any]
45 ) -> dict[str, str]:
46  """Validate the user input allows us to connect."""
47  ayla_api = get_ayla_api(
48  username=data[CONF_USERNAME],
49  password=data[CONF_PASSWORD],
50  websession=async_get_clientsession(hass),
51  europe=(data[CONF_REGION] == SHARKIQ_REGION_EUROPE),
52  )
53 
54  try:
55  async with asyncio.timeout(10):
56  LOGGER.debug("Initialize connection to Ayla networks API")
57  await ayla_api.async_sign_in()
58  except (TimeoutError, aiohttp.ClientError, TypeError) as error:
59  LOGGER.error(error)
60  raise CannotConnect(
61  "Unable to connect to SharkIQ services. Check your region settings."
62  ) from error
63  except SharkIqAuthError as error:
64  LOGGER.error(error)
65  raise InvalidAuth(
66  "Username or password incorrect. Please check your credentials."
67  ) from error
68  except Exception as error:
69  LOGGER.exception("Unexpected exception")
70  LOGGER.error(error)
71  raise UnknownAuth(
72  "An unknown error occurred. Check your region settings and open an issue on Github if the issue persists."
73  ) from error
74 
75  # Return info that you want to store in the config entry.
76  return {"title": data[CONF_USERNAME]}
77 
78 
79 class SharkIqConfigFlow(ConfigFlow, domain=DOMAIN):
80  """Handle a config flow for Shark IQ."""
81 
82  VERSION = 1
83 
85  self, user_input: Mapping[str, Any]
86  ) -> tuple[dict[str, str] | None, dict[str, str]]:
87  """Validate form input."""
88  errors = {}
89  info = None
90 
91  # noinspection PyBroadException
92  try:
93  info = await _validate_input(self.hass, user_input)
94  except CannotConnect:
95  errors["base"] = "cannot_connect"
96  except InvalidAuth:
97  errors["base"] = "invalid_auth"
98  except UnknownAuth:
99  errors["base"] = "unknown"
100  return info, errors
101 
102  async def async_step_user(
103  self, user_input: dict[str, str] | None = None
104  ) -> ConfigFlowResult:
105  """Handle the initial step."""
106  errors: dict[str, str] = {}
107  if user_input is not None:
108  info, errors = await self._async_validate_input_async_validate_input(user_input)
109  if info:
110  await self.async_set_unique_idasync_set_unique_id(user_input[CONF_USERNAME])
111  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
112  return self.async_create_entryasync_create_entryasync_create_entry(title=info["title"], data=user_input)
113 
114  return self.async_show_formasync_show_formasync_show_form(
115  step_id="user", data_schema=SHARKIQ_SCHEMA, errors=errors
116  )
117 
118  async def async_step_reauth(
119  self, entry_data: Mapping[str, Any]
120  ) -> ConfigFlowResult:
121  """Handle re-auth if login is invalid."""
122  return await self.async_step_reauth_confirmasync_step_reauth_confirm()
123 
125  self, user_input: dict[str, Any] | None = None
126  ) -> ConfigFlowResult:
127  """Handle a flow initiated by reauthentication."""
128  errors: dict[str, str] = {}
129 
130  if user_input is not None:
131  _, errors = await self._async_validate_input_async_validate_input(user_input)
132 
133  if not errors:
134  errors = {"base": "unknown"}
135  if entry := await self.async_set_unique_idasync_set_unique_id(self.unique_idunique_id):
136  self.hass.config_entries.async_update_entry(entry, data=user_input)
137  return self.async_abortasync_abortasync_abort(reason="reauth_successful")
138 
139  if errors["base"] != "invalid_auth":
140  return self.async_abortasync_abortasync_abort(reason=errors["base"])
141 
142  return self.async_show_formasync_show_formasync_show_form(
143  step_id="reauth_confirm",
144  data_schema=SHARKIQ_SCHEMA,
145  errors=errors,
146  )
147 
148 
150  """Error to indicate we cannot connect."""
151 
152 
154  """Error to indicate there is invalid auth."""
155 
156 
158  """Error to indicate there is an uncaught auth error."""
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:126
tuple[dict[str, str]|None, dict[str, str]] _async_validate_input(self, Mapping[str, Any] user_input)
Definition: config_flow.py:86
ConfigFlowResult async_step_user(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:104
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
Definition: config_flow.py:120
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)
_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)
dict[str, str] _validate_input(HomeAssistant hass, Mapping[str, Any] data)
Definition: config_flow.py:45
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)