Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Electra Air Conditioner integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from electrasmart.api import STATUS_SUCCESS, Attributes, ElectraAPI, ElectraApiError
9 from electrasmart.api.utils import generate_imei
10 import voluptuous as vol
11 
12 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
13 from homeassistant.const import CONF_TOKEN
14 from homeassistant.helpers.aiohttp_client import async_get_clientsession
15 
16 from .const import CONF_IMEI, CONF_OTP, CONF_PHONE_NUMBER, DOMAIN
17 
18 _LOGGER = logging.getLogger(__name__)
19 
20 
21 class ElectraSmartConfigFlow(ConfigFlow, domain=DOMAIN):
22  """Handle a config flow for Electra Air Conditioner."""
23 
24  VERSION = 1
25 
26  def __init__(self) -> None:
27  """Device settings."""
28  self._phone_number_phone_number: str | None = None
29  self._description_placeholders_description_placeholders = None
30  self._otp_otp: str | None = None
31  self._imei_imei: str | None = None
32  self._token_token: str | None = None
33  self._api_api: ElectraAPI | None = None
34 
35  async def async_step_user(
36  self, user_input: dict[str, Any] | None = None
37  ) -> ConfigFlowResult:
38  """Handle the initial step."""
39 
40  if not self._api_api:
41  self._api_api = ElectraAPI(async_get_clientsession(self.hass))
42 
43  errors: dict[str, Any] = {}
44 
45  if user_input is None:
46  return self._show_setup_form_show_setup_form(user_input, errors, "user")
47 
48  return await self._validate_phone_number_validate_phone_number(user_input)
49 
51  self,
52  user_input: dict[str, str] | None = None,
53  errors: dict[str, str] | None = None,
54  step_id: str = "user",
55  ) -> ConfigFlowResult:
56  """Show the setup form to the user."""
57  if user_input is None:
58  user_input = {}
59 
60  if step_id == "user":
61  schema = {
62  vol.Required(
63  CONF_PHONE_NUMBER, default=user_input.get(CONF_PHONE_NUMBER, "")
64  ): str
65  }
66  else:
67  schema = {vol.Required(CONF_OTP, default=user_input.get(CONF_OTP, "")): str}
68 
69  return self.async_show_formasync_show_formasync_show_form(
70  step_id=step_id,
71  data_schema=vol.Schema(schema),
72  errors=errors or {},
73  description_placeholders=self._description_placeholders_description_placeholders,
74  )
75 
77  self, user_input: dict[str, str]
78  ) -> ConfigFlowResult:
79  """Check if config is valid and create entry if so."""
80 
81  self._phone_number_phone_number = user_input[CONF_PHONE_NUMBER]
82  self._imei_imei = generate_imei()
83 
84  # Check if already configured
85  if self.unique_idunique_id is None:
86  await self.async_set_unique_idasync_set_unique_id(self._phone_number_phone_number)
87  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
88 
89  assert isinstance(self._api_api, ElectraAPI)
90 
91  try:
92  resp = await self._api_api.generate_new_token(self._phone_number_phone_number, self._imei_imei)
93  except ElectraApiError as exp:
94  _LOGGER.error("Failed to connect to API: %s", exp)
95  return self._show_setup_form_show_setup_form(user_input, {"base": "cannot_connect"}, "user")
96 
97  if resp[Attributes.STATUS] == STATUS_SUCCESS:
98  if resp[Attributes.DATA][Attributes.RES] != STATUS_SUCCESS:
99  return self._show_setup_form_show_setup_form(
100  user_input, {CONF_PHONE_NUMBER: "invalid_phone_number"}, "user"
101  )
102 
103  return await self.async_step_one_time_passwordasync_step_one_time_password()
104 
106  self, user_input: dict[str, str]
107  ) -> ConfigFlowResult:
108  self._otp_otp = user_input[CONF_OTP]
109 
110  assert isinstance(self._api_api, ElectraAPI)
111  assert isinstance(self._imei_imei, str)
112  assert isinstance(self._phone_number_phone_number, str)
113  assert isinstance(self._otp_otp, str)
114 
115  try:
116  resp = await self._api_api.validate_one_time_password(
117  self._otp_otp, self._imei_imei, self._phone_number_phone_number
118  )
119  except ElectraApiError as exp:
120  _LOGGER.error("Failed to connect to API: %s", exp)
121  return self._show_setup_form_show_setup_form(
122  user_input, {"base": "cannot_connect"}, CONF_OTP
123  )
124 
125  if resp[Attributes.DATA][Attributes.RES] == STATUS_SUCCESS:
126  self._token_token = resp[Attributes.DATA][Attributes.TOKEN]
127 
128  data = {
129  CONF_TOKEN: self._token_token,
130  CONF_IMEI: self._imei_imei,
131  CONF_PHONE_NUMBER: self._phone_number_phone_number,
132  }
133  return self.async_create_entryasync_create_entryasync_create_entry(title=self._phone_number_phone_number, data=data)
134  return self._show_setup_form_show_setup_form(user_input, {CONF_OTP: "invalid_auth"}, CONF_OTP)
135 
137  self,
138  user_input: dict[str, Any] | None = None,
139  errors: dict[str, str] | None = None,
140  ) -> ConfigFlowResult:
141  """Ask the verification code to the user."""
142  if errors is None:
143  errors = {}
144 
145  if user_input is None:
146  return await self._show_otp_form_show_otp_form(errors)
147 
148  return await self._validate_one_time_password_validate_one_time_password(user_input)
149 
150  async def _show_otp_form(
151  self,
152  errors: dict[str, str] | None = None,
153  ) -> ConfigFlowResult:
154  """Show the verification_code form to the user."""
155 
156  return self.async_show_formasync_show_formasync_show_form(
157  step_id=CONF_OTP,
158  data_schema=vol.Schema({vol.Required(CONF_OTP): str}),
159  errors=errors or {},
160  )
ConfigFlowResult _show_otp_form(self, dict[str, str]|None errors=None)
Definition: config_flow.py:153
ConfigFlowResult _validate_phone_number(self, dict[str, str] user_input)
Definition: config_flow.py:78
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:37
ConfigFlowResult _show_setup_form(self, dict[str, str]|None user_input=None, dict[str, str]|None errors=None, str step_id="user")
Definition: config_flow.py:55
ConfigFlowResult async_step_one_time_password(self, dict[str, Any]|None user_input=None, dict[str, str]|None errors=None)
Definition: config_flow.py:140
ConfigFlowResult _validate_one_time_password(self, dict[str, str] user_input)
Definition: config_flow.py:107
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)
_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)
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)