1 """Config flow for EZVIZ."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
7 from typing
import TYPE_CHECKING, Any
9 from pyezviz.client
import EzvizClient
10 from pyezviz.exceptions
import (
12 EzvizAuthVerificationCode,
17 from pyezviz.test_cam_rtsp
import TestRTSPAuth
18 import voluptuous
as vol
41 CONF_FFMPEG_ARGUMENTS,
44 DEFAULT_CAMERA_USERNAME,
45 DEFAULT_FFMPEG_ARGUMENTS,
52 _LOGGER = logging.getLogger(__name__)
54 CONF_FFMPEG_ARGUMENTS: DEFAULT_FFMPEG_ARGUMENTS,
55 CONF_TIMEOUT: DEFAULT_TIMEOUT,
60 """Try to login to EZVIZ cloud account and return token."""
64 ezviz_client = EzvizClient(
68 data.get(CONF_TIMEOUT, DEFAULT_TIMEOUT),
71 ezviz_token = ezviz_client.login()
74 CONF_SESSION_ID: ezviz_token[CONF_SESSION_ID],
75 CONF_RFSESSION_ID: ezviz_token[CONF_RFSESSION_ID],
76 CONF_URL: ezviz_token[
"api_url"],
77 CONF_TYPE: ATTR_TYPE_CLOUD,
82 """Try DESCRIBE on RTSP camera with credentials."""
84 test_rtsp = TestRTSPAuth(
85 data[CONF_IP_ADDRESS], data[CONF_USERNAME], data[CONF_PASSWORD]
92 """Handle a config flow for EZVIZ."""
102 """Try DESCRIBE on RTSP camera with credentials."""
106 CONF_SESSION_ID:
None,
107 CONF_RFSESSION_ID:
None,
110 ezviz_timeout = DEFAULT_TIMEOUT
113 if item.data.get(CONF_TYPE) == ATTR_TYPE_CLOUD:
115 CONF_SESSION_ID: item.data.get(CONF_SESSION_ID),
116 CONF_RFSESSION_ID: item.data.get(CONF_RFSESSION_ID),
117 "api_url": item.data.get(CONF_URL),
119 ezviz_timeout = item.data.get(CONF_TIMEOUT, DEFAULT_TIMEOUT)
122 if ezviz_token.get(CONF_SESSION_ID)
is None:
125 ezviz_client = EzvizClient(token=ezviz_token, timeout=ezviz_timeout)
129 await self.hass.async_add_executor_job(ezviz_client.login)
132 await self.hass.async_add_executor_job(
133 ezviz_client.get_detection_sensibility, data[ATTR_SERIAL]
137 await self.hass.async_add_executor_job(_test_camera_rtsp_creds, data)
140 title=data[ATTR_SERIAL],
142 CONF_USERNAME: data[CONF_USERNAME],
143 CONF_PASSWORD: data[CONF_PASSWORD],
144 CONF_TYPE: ATTR_TYPE_CAMERA,
146 options=DEFAULT_OPTIONS,
152 """Get the options flow for this handler."""
156 self, user_input: dict[str, Any] |
None =
None
157 ) -> ConfigFlowResult:
158 """Handle a flow initiated by the user."""
163 if item.data.get(CONF_TYPE) == ATTR_TYPE_CLOUD:
169 if user_input
is not None:
173 if user_input[CONF_URL] == CONF_CUSTOMIZE:
179 auth_data = await self.hass.async_add_executor_job(
180 _validate_and_create_auth, user_input
184 errors[
"base"] =
"invalid_host"
187 errors[
"base"] =
"cannot_connect"
189 except EzvizAuthVerificationCode:
190 errors[
"base"] =
"mfa_required"
193 errors[
"base"] =
"invalid_auth"
196 _LOGGER.exception(
"Unexpected exception")
201 title=user_input[CONF_USERNAME],
203 options=DEFAULT_OPTIONS,
206 data_schema = vol.Schema(
208 vol.Required(CONF_USERNAME): str,
209 vol.Required(CONF_PASSWORD): str,
210 vol.Required(CONF_URL, default=EU_URL): vol.In(
211 [EU_URL, RUSSIA_URL, CONF_CUSTOMIZE]
217 step_id=
"user", data_schema=data_schema, errors=errors
221 self, user_input: dict[str, Any] |
None =
None
222 ) -> ConfigFlowResult:
223 """Handle a flow initiated by the user for custom region url."""
227 if user_input
is not None:
228 user_input[CONF_USERNAME] = self.
usernameusername
229 user_input[CONF_PASSWORD] = self.
passwordpassword
232 auth_data = await self.hass.async_add_executor_job(
233 _validate_and_create_auth, user_input
237 errors[
"base"] =
"invalid_host"
240 errors[
"base"] =
"cannot_connect"
242 except EzvizAuthVerificationCode:
243 errors[
"base"] =
"mfa_required"
246 errors[
"base"] =
"invalid_auth"
249 _LOGGER.exception(
"Unexpected exception")
254 title=user_input[CONF_USERNAME],
256 options=DEFAULT_OPTIONS,
259 data_schema_custom_url = vol.Schema(
261 vol.Required(CONF_URL, default=EU_URL): str,
266 step_id=
"user_custom_url", data_schema=data_schema_custom_url, errors=errors
270 self, discovery_info: dict[str, Any]
271 ) -> ConfigFlowResult:
272 """Handle a flow for discovered camera without rtsp config entry."""
279 assert self.
unique_idunique_id
is not None
280 self.context[
"title_placeholders"] = {ATTR_SERIAL: self.
unique_idunique_id}
286 self, user_input: dict[str, Any] |
None =
None
287 ) -> ConfigFlowResult:
288 """Confirm and create entry from discovery step."""
291 if user_input
is not None:
292 user_input[ATTR_SERIAL] = self.
unique_idunique_id
293 user_input[CONF_IP_ADDRESS] = self.
ip_addressip_address
297 except (InvalidHost, InvalidURL):
298 errors[
"base"] =
"invalid_host"
300 except EzvizAuthVerificationCode:
301 errors[
"base"] =
"mfa_required"
303 except (PyEzvizError, AuthTestResultFailed):
304 errors[
"base"] =
"invalid_auth"
307 _LOGGER.exception(
"Unexpected exception")
310 discovered_camera_schema = vol.Schema(
312 vol.Required(CONF_USERNAME, default=DEFAULT_CAMERA_USERNAME): str,
313 vol.Required(CONF_PASSWORD): str,
319 data_schema=discovered_camera_schema,
321 description_placeholders={
328 self, entry_data: Mapping[str, Any]
329 ) -> ConfigFlowResult:
330 """Handle a flow for reauthentication with password."""
335 self, user_input: dict[str, Any] |
None =
None
336 ) -> ConfigFlowResult:
337 """Handle a Confirm flow for reauthentication with password."""
343 if item.data.get(CONF_TYPE) == ATTR_TYPE_CLOUD:
344 self.context[
"title_placeholders"] = {ATTR_SERIAL: item.title}
350 if user_input
is not None:
351 user_input[CONF_URL] = entry.data[CONF_URL]
354 auth_data = await self.hass.async_add_executor_job(
355 _validate_and_create_auth, user_input
358 except (InvalidHost, InvalidURL):
359 errors[
"base"] =
"invalid_host"
361 except EzvizAuthVerificationCode:
362 errors[
"base"] =
"mfa_required"
364 except (PyEzvizError, AuthTestResultFailed):
365 errors[
"base"] =
"invalid_auth"
368 _LOGGER.exception(
"Unexpected exception")
377 data_schema = vol.Schema(
379 vol.Required(CONF_USERNAME, default=entry.title): vol.In([entry.title]),
380 vol.Required(CONF_PASSWORD): str,
385 step_id=
"reauth_confirm",
386 data_schema=data_schema,
392 """Handle EZVIZ client options."""
395 self, user_input: dict[str, Any] |
None =
None
396 ) -> ConfigFlowResult:
397 """Manage EZVIZ options."""
398 if user_input
is not None:
401 options = vol.Schema(
406 CONF_TIMEOUT, DEFAULT_TIMEOUT
410 CONF_FFMPEG_ARGUMENTS,
412 CONF_FFMPEG_ARGUMENTS, DEFAULT_FFMPEG_ARGUMENTS
418 return self.
async_show_formasync_show_form(step_id=
"init", data_schema=options)
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_user_custom_url(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
EzvizOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult _validate_and_create_camera_rtsp(self, dict data)
ConfigFlowResult async_step_integration_discovery(self, dict[str, Any] discovery_info)
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
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)
list[ConfigEntry] _async_current_entries(self, bool|None include_ignore=None)
ConfigFlowResult async_update_reload_and_abort(self, ConfigEntry entry, *str|None|UndefinedType unique_id=UNDEFINED, str|UndefinedType title=UNDEFINED, Mapping[str, Any]|UndefinedType data=UNDEFINED, Mapping[str, Any]|UndefinedType data_updates=UNDEFINED, Mapping[str, Any]|UndefinedType options=UNDEFINED, str|UndefinedType reason=UNDEFINED, bool reload_even_if_entry_is_unchanged=True)
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)
ConfigEntry config_entry(self)
None config_entry(self, ConfigEntry value)
_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)
None _test_camera_rtsp_creds(dict data)
dict[str, Any] _validate_and_create_auth(dict data)