1 """Config flow for AVM FRITZ!SmartHome."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
7 from typing
import Any, Self
8 from urllib.parse
import urlparse
10 from pyfritzhome
import Fritzhome, LoginError
11 from requests.exceptions
import HTTPError
12 import voluptuous
as vol
18 from .const
import DEFAULT_HOST, DEFAULT_USERNAME, DOMAIN
20 DATA_SCHEMA_USER = vol.Schema(
22 vol.Required(CONF_HOST, default=DEFAULT_HOST): str,
23 vol.Required(CONF_USERNAME, default=DEFAULT_USERNAME): str,
24 vol.Required(CONF_PASSWORD): str,
28 DATA_SCHEMA_CONFIRM = vol.Schema(
30 vol.Required(CONF_USERNAME, default=DEFAULT_USERNAME): str,
31 vol.Required(CONF_PASSWORD): str,
35 RESULT_INVALID_AUTH =
"invalid_auth"
36 RESULT_NO_DEVICES_FOUND =
"no_devices_found"
37 RESULT_NOT_SUPPORTED =
"not_supported"
38 RESULT_SUCCESS =
"success"
42 """Handle a AVM FRITZ!SmartHome config flow."""
49 """Initialize flow."""
50 self.
_host_host: str |
None =
None
51 self.
_password_password: str |
None =
None
52 self.
_username_username: str |
None =
None
58 CONF_HOST: self.
_host_host,
65 """Try to connect and check auth."""
66 return await self.hass.async_add_executor_job(self.
_try_connect_try_connect)
69 """Try to connect and check auth."""
75 fritzbox.get_device_elements()
78 return RESULT_INVALID_AUTH
80 return RESULT_NOT_SUPPORTED
82 return RESULT_NO_DEVICES_FOUND
86 self, user_input: dict[str, Any] |
None =
None
87 ) -> ConfigFlowResult:
88 """Handle a flow initialized by the user."""
91 if user_input
is not None:
94 self.
_host_host = user_input[CONF_HOST]
101 if result == RESULT_SUCCESS:
103 if result != RESULT_INVALID_AUTH:
105 errors[
"base"] = result
108 step_id=
"user", data_schema=DATA_SCHEMA_USER, errors=errors
112 self, discovery_info: ssdp.SsdpServiceInfo
113 ) -> ConfigFlowResult:
114 """Handle a flow initialized by discovery."""
115 host = urlparse(discovery_info.ssdp_location).hostname
116 assert isinstance(host, str)
119 ipaddress.ip_address(host).version == 6
120 and ipaddress.ip_address(host).is_link_local
124 if uuid := discovery_info.upnp.get(ssdp.ATTR_UPNP_UDN):
125 if uuid.startswith(
"uuid:"):
130 self.
_host_host = host
131 if self.hass.config_entries.flow.async_has_matching_flow(self):
136 if entry.data[CONF_HOST] == host:
137 if uuid
and not entry.unique_id:
138 self.hass.config_entries.async_update_entry(entry, unique_id=uuid)
141 self.
_name_name =
str(discovery_info.upnp.get(ssdp.ATTR_UPNP_FRIENDLY_NAME)
or host)
143 self.context[
"title_placeholders"] = {
"name": self.
_name_name}
147 """Return True if other_flow is matching this flow."""
148 return other_flow._host == self.
_host_host
151 self, user_input: dict[str, Any] |
None =
None
152 ) -> ConfigFlowResult:
153 """Handle user-confirmation of discovered node."""
156 if user_input
is not None:
157 self.
_password_password = user_input[CONF_PASSWORD]
158 self.
_username_username = user_input[CONF_USERNAME]
161 if result == RESULT_SUCCESS:
163 if result != RESULT_INVALID_AUTH:
165 errors[
"base"] = result
169 data_schema=DATA_SCHEMA_CONFIRM,
170 description_placeholders={
"name": self.
_name_name},
175 self, entry_data: Mapping[str, Any]
176 ) -> ConfigFlowResult:
177 """Trigger a reauthentication flow."""
178 self.
_host_host = entry_data[CONF_HOST]
179 self.
_name_name =
str(entry_data[CONF_HOST])
180 self.
_username_username = entry_data[CONF_USERNAME]
185 self, user_input: dict[str, Any] |
None =
None
186 ) -> ConfigFlowResult:
187 """Handle reauthorization flow."""
190 if user_input
is not None:
191 self.
_password_password = user_input[CONF_PASSWORD]
192 self.
_username_username = user_input[CONF_USERNAME]
196 if result == RESULT_SUCCESS:
200 CONF_HOST: self.
_host_host,
205 if result != RESULT_INVALID_AUTH:
207 errors[
"base"] = result
210 step_id=
"reauth_confirm",
211 data_schema=vol.Schema(
213 vol.Required(CONF_USERNAME, default=self.
_username_username): str,
214 vol.Required(CONF_PASSWORD): str,
217 description_placeholders={
"name": self.
_name_name},
222 self, user_input: dict[str, Any] |
None =
None
223 ) -> ConfigFlowResult:
224 """Handle a reconfiguration flow initialized by the user."""
227 if user_input
is not None:
228 self.
_host_host = user_input[CONF_HOST]
231 self.
_username_username = reconfigure_entry.data[CONF_USERNAME]
232 self.
_password_password = reconfigure_entry.data[CONF_PASSWORD]
236 if result == RESULT_SUCCESS:
239 data_updates={CONF_HOST: self.
_host_host},
241 errors[
"base"] = result
245 step_id=
"reconfigure",
246 data_schema=vol.Schema(
248 vol.Required(CONF_HOST, default=host): str,
251 description_placeholders={
"name": host},
ConfigFlowResult async_step_ssdp(self, ssdp.SsdpServiceInfo discovery_info)
ConfigFlowResult async_step_reconfigure(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
str async_try_connect(self)
ConfigFlowResult _get_entry(self, str name)
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
bool is_matching(self, Self other_flow)
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
ConfigEntry _get_reauth_entry(self)
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)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=None)
ConfigEntry _get_reconfigure_entry(self)
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)