Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow to configure StarLine component."""
2 
3 from __future__ import annotations
4 
5 from starline import StarlineAuth
6 import voluptuous as vol
7 
8 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
9 from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
10 from homeassistant.core import callback
11 
12 from .const import (
13  _LOGGER,
14  CONF_APP_ID,
15  CONF_APP_SECRET,
16  CONF_CAPTCHA_CODE,
17  CONF_MFA_CODE,
18  DATA_EXPIRES,
19  DATA_SLID_TOKEN,
20  DATA_SLNET_TOKEN,
21  DATA_USER_ID,
22  DOMAIN,
23  ERROR_AUTH_APP,
24  ERROR_AUTH_MFA,
25  ERROR_AUTH_USER,
26 )
27 
28 
29 class StarlineFlowHandler(ConfigFlow, domain=DOMAIN):
30  """Handle a StarLine config flow."""
31 
32  VERSION = 1
33 
34  _app_code: str
35  _app_token: str
36  _captcha_image: str
37  _phone_number: str
38 
39  def __init__(self) -> None:
40  """Initialize flow."""
41  self._app_id_app_id: str | None = None
42  self._app_secret_app_secret: str | None = None
43  self._username_username: str | None = None
44  self._password_password: str | None = None
45  self._mfa_code_mfa_code: str | None = None
46 
47  self._user_slid_user_slid = None
48  self._user_id_user_id = None
49  self._slnet_token_slnet_token = None
50  self._slnet_token_expires_slnet_token_expires = None
51  self._captcha_sid_captcha_sid: str | None = None
52  self._captcha_code_captcha_code: str | None = None
53 
54  self._auth_auth = StarlineAuth()
55 
56  async def async_step_user(
57  self, user_input: dict[str, str] | None = None
58  ) -> ConfigFlowResult:
59  """Handle a flow initialized by the user."""
60  return await self.async_step_auth_appasync_step_auth_app(user_input)
61 
63  self, user_input: dict[str, str] | None = None
64  ) -> ConfigFlowResult:
65  """Authenticate application step."""
66  if user_input is not None:
67  self._app_id_app_id = user_input[CONF_APP_ID]
68  self._app_secret_app_secret = user_input[CONF_APP_SECRET]
69  return await self._async_authenticate_app_async_authenticate_app()
70  return self._async_form_auth_app_async_form_auth_app()
71 
73  self, user_input: dict[str, str] | None = None
74  ) -> ConfigFlowResult:
75  """Authenticate user step."""
76  if user_input is not None:
77  self._username_username = user_input[CONF_USERNAME]
78  self._password_password = user_input[CONF_PASSWORD]
79  return await self._async_authenticate_user_async_authenticate_user()
80  return self._async_form_auth_user_async_form_auth_user()
81 
83  self, user_input: dict[str, str] | None = None
84  ) -> ConfigFlowResult:
85  """Authenticate mfa step."""
86  if user_input is not None:
87  self._mfa_code_mfa_code = user_input[CONF_MFA_CODE]
88  return await self._async_authenticate_user_async_authenticate_user()
89  return self._async_form_auth_mfa_async_form_auth_mfa()
90 
92  self, user_input: dict[str, str] | None = None
93  ) -> ConfigFlowResult:
94  """Captcha verification step."""
95  if user_input is not None:
96  self._captcha_code_captcha_code = user_input[CONF_CAPTCHA_CODE]
97  return await self._async_authenticate_user_async_authenticate_user()
98  return self._async_form_auth_captcha_async_form_auth_captcha()
99 
100  @callback
101  def _async_form_auth_app(self, error: str | None = None) -> ConfigFlowResult:
102  """Authenticate application form."""
103  errors: dict[str, str] = {}
104  if error is not None:
105  errors["base"] = error
106 
107  return self.async_show_formasync_show_formasync_show_form(
108  step_id="auth_app",
109  data_schema=vol.Schema(
110  {
111  vol.Required(
112  CONF_APP_ID, default=self._app_id_app_id or vol.UNDEFINED
113  ): str,
114  vol.Required(
115  CONF_APP_SECRET, default=self._app_secret_app_secret or vol.UNDEFINED
116  ): str,
117  }
118  ),
119  errors=errors,
120  )
121 
122  @callback
123  def _async_form_auth_user(self, error: str | None = None) -> ConfigFlowResult:
124  """Authenticate user form."""
125  errors = {}
126  if error is not None:
127  errors["base"] = error
128 
129  return self.async_show_formasync_show_formasync_show_form(
130  step_id="auth_user",
131  data_schema=vol.Schema(
132  {
133  vol.Required(
134  CONF_USERNAME, default=self._username_username or vol.UNDEFINED
135  ): str,
136  vol.Required(
137  CONF_PASSWORD, default=self._password_password or vol.UNDEFINED
138  ): str,
139  }
140  ),
141  errors=errors,
142  )
143 
144  @callback
145  def _async_form_auth_mfa(self, error: str | None = None) -> ConfigFlowResult:
146  """Authenticate mfa form."""
147  errors = {}
148  if error is not None:
149  errors["base"] = error
150 
151  return self.async_show_formasync_show_formasync_show_form(
152  step_id="auth_mfa",
153  data_schema=vol.Schema(
154  {
155  vol.Required(
156  CONF_MFA_CODE, default=self._mfa_code_mfa_code or vol.UNDEFINED
157  ): str
158  }
159  ),
160  errors=errors,
161  description_placeholders={"phone_number": self._phone_number_phone_number},
162  )
163 
164  @callback
165  def _async_form_auth_captcha(self, error: str | None = None) -> ConfigFlowResult:
166  """Captcha verification form."""
167  errors = {}
168  if error is not None:
169  errors["base"] = error
170 
171  return self.async_show_formasync_show_formasync_show_form(
172  step_id="auth_captcha",
173  data_schema=vol.Schema(
174  {
175  vol.Required(
176  CONF_CAPTCHA_CODE, default=self._captcha_code_captcha_code or vol.UNDEFINED
177  ): str
178  }
179  ),
180  errors=errors,
181  description_placeholders={
182  "captcha_img": '<img src="' + self._captcha_image_captcha_image + '"/>'
183  },
184  )
185 
187  self, error: str | None = None
188  ) -> ConfigFlowResult:
189  """Authenticate application."""
190  try:
191  self._app_code_app_code = await self.hass.async_add_executor_job(
192  self._auth_auth.get_app_code, self._app_id_app_id, self._app_secret_app_secret
193  )
194  self._app_token_app_token = await self.hass.async_add_executor_job(
195  self._auth_auth.get_app_token, self._app_id_app_id, self._app_secret_app_secret, self._app_code_app_code
196  )
197  return self._async_form_auth_user_async_form_auth_user(error)
198  except Exception as err: # noqa: BLE001
199  _LOGGER.error("Error auth StarLine: %s", err)
200  return self._async_form_auth_app_async_form_auth_app(ERROR_AUTH_APP)
201 
203  self, error: str | None = None
204  ) -> ConfigFlowResult:
205  """Authenticate user."""
206  try:
207  state, data = await self.hass.async_add_executor_job(
208  self._auth_auth.get_slid_user_token,
209  self._app_token_app_token,
210  self._username_username,
211  self._password_password,
212  self._mfa_code_mfa_code,
213  self._captcha_sid_captcha_sid,
214  self._captcha_code_captcha_code,
215  )
216 
217  if state == 1:
218  self._user_slid_user_slid = data["user_token"]
219  return await self._async_get_entry_async_get_entry()
220 
221  if "phone" in data:
222  self._phone_number_phone_number = data["phone"]
223  if state == 0:
224  error = ERROR_AUTH_MFA
225  return self._async_form_auth_mfa_async_form_auth_mfa(error)
226 
227  if "captchaSid" in data:
228  self._captcha_sid_captcha_sid = data["captchaSid"]
229  self._captcha_image_captcha_image = data["captchaImg"]
230  return self._async_form_auth_captcha_async_form_auth_captcha(error)
231 
232  raise Exception(data) # noqa: TRY002, TRY301
233  except Exception as err: # noqa: BLE001
234  _LOGGER.error("Error auth user: %s", err)
235  return self._async_form_auth_user_async_form_auth_user(ERROR_AUTH_USER)
236 
237  async def _async_get_entry(self) -> ConfigFlowResult:
238  """Create entry."""
239  (
240  self._slnet_token_slnet_token,
241  self._slnet_token_expires_slnet_token_expires,
242  self._user_id_user_id,
243  ) = await self.hass.async_add_executor_job(
244  self._auth_auth.get_user_id, self._user_slid_user_slid
245  )
246 
247  return self.async_create_entryasync_create_entryasync_create_entry(
248  title=f"Application {self._app_id}",
249  data={
250  DATA_USER_ID: self._user_id_user_id,
251  DATA_SLNET_TOKEN: self._slnet_token_slnet_token,
252  DATA_SLID_TOKEN: self._user_slid_user_slid,
253  DATA_EXPIRES: self._slnet_token_expires_slnet_token_expires,
254  },
255  )
ConfigFlowResult _async_form_auth_captcha(self, str|None error=None)
Definition: config_flow.py:165
ConfigFlowResult _async_authenticate_app(self, str|None error=None)
Definition: config_flow.py:188
ConfigFlowResult async_step_auth_app(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:64
ConfigFlowResult async_step_auth_user(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:74
ConfigFlowResult _async_form_auth_user(self, str|None error=None)
Definition: config_flow.py:123
ConfigFlowResult _async_form_auth_mfa(self, str|None error=None)
Definition: config_flow.py:145
ConfigFlowResult async_step_auth_mfa(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:84
ConfigFlowResult async_step_auth_captcha(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:93
ConfigFlowResult _async_authenticate_user(self, str|None error=None)
Definition: config_flow.py:204
ConfigFlowResult _async_form_auth_app(self, str|None error=None)
Definition: config_flow.py:101
ConfigFlowResult async_step_user(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:58
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)