Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Sensoterra integration."""
2 
3 from __future__ import annotations
4 
5 from datetime import datetime, timedelta
6 from typing import Any
7 
8 from jwt import DecodeError, decode
9 from sensoterra.customerapi import (
10  CustomerApi,
11  InvalidAuth as StInvalidAuth,
12  Timeout as StTimeout,
13 )
14 import voluptuous as vol
15 
16 from homeassistant.config_entries import SOURCE_USER, ConfigFlow, ConfigFlowResult
17 from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_TOKEN
19  TextSelector,
20  TextSelectorConfig,
21  TextSelectorType,
22 )
23 
24 from .const import DOMAIN, LOGGER, TOKEN_EXPIRATION_DAYS
25 
26 STEP_USER_DATA_SCHEMA = vol.Schema(
27  {
28  vol.Required(CONF_EMAIL): TextSelector(
29  TextSelectorConfig(type=TextSelectorType.EMAIL, autocomplete="email")
30  ),
31  vol.Required(CONF_PASSWORD): TextSelector(
32  TextSelectorConfig(type=TextSelectorType.PASSWORD)
33  ),
34  }
35 )
36 
37 
38 class SensoterraConfigFlow(ConfigFlow, domain=DOMAIN):
39  """Handle a config flow for Sensoterra."""
40 
41  async def async_step_user(
42  self, user_input: dict[str, Any] | None = None
43  ) -> ConfigFlowResult:
44  """Create hub entry based on config flow."""
45  errors: dict[str, str] = {}
46 
47  if user_input is not None:
48  api = CustomerApi(user_input[CONF_EMAIL], user_input[CONF_PASSWORD])
49  # We need a unique tag per HA instance
50  uuid = self.hass.data["core.uuid"]
51  expiration = datetime.now() + timedelta(TOKEN_EXPIRATION_DAYS)
52 
53  try:
54  token: str = await api.get_token(
55  f"Home Assistant {uuid}", "READONLY", expiration
56  )
57  decoded_token = decode(
58  token, algorithms=["HS256"], options={"verify_signature": False}
59  )
60 
61  except StInvalidAuth as exp:
62  LOGGER.error(
63  "Login attempt with %s: %s", user_input[CONF_EMAIL], exp.message
64  )
65  errors["base"] = "invalid_auth"
66  except StTimeout:
67  LOGGER.error("Login attempt with %s: time out", user_input[CONF_EMAIL])
68  errors["base"] = "cannot_connect"
69  except DecodeError:
70  LOGGER.error("Login attempt with %s: bad token", user_input[CONF_EMAIL])
71  errors["base"] = "invalid_access_token"
72  else:
73  device_unique_id = decoded_token["sub"]
74  await self.async_set_unique_idasync_set_unique_id(device_unique_id)
75  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
76  return self.async_create_entryasync_create_entryasync_create_entry(
77  title=user_input[CONF_EMAIL],
78  data={
79  CONF_TOKEN: token,
80  CONF_EMAIL: user_input[CONF_EMAIL],
81  },
82  )
83 
84  return self.async_show_formasync_show_formasync_show_form(
85  step_id=SOURCE_USER,
86  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
87  STEP_USER_DATA_SCHEMA, user_input
88  ),
89  errors=errors,
90  )
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:43
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)
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)