1 """Config flow to configure deCONZ component."""
3 from __future__
import annotations
6 from collections.abc
import Mapping
8 from pprint
import pformat
9 from typing
import Any, cast
10 from urllib.parse
import urlparse
12 from pydeconz.errors
import LinkButtonNotPressed, RequestError, ResponseError
13 from pydeconz.gateway
import DeconzSession
14 from pydeconz.utils
import (
16 discovery
as deconz_discovery,
17 get_bridge_id
as deconz_get_bridge_id,
20 import voluptuous
as vol
36 CONF_ALLOW_CLIP_SENSOR,
37 CONF_ALLOW_DECONZ_GROUPS,
38 CONF_ALLOW_NEW_DEVICES,
39 DEFAULT_ALLOW_CLIP_SENSOR,
40 DEFAULT_ALLOW_DECONZ_GROUPS,
41 DEFAULT_ALLOW_NEW_DEVICES,
44 HASSIO_CONFIGURATION_URL,
47 from .hub
import DeconzHub
49 DECONZ_MANUFACTURERURL =
"http://www.dresden-elektronik.de"
50 CONF_SERIAL =
"serial"
51 CONF_MANUAL_INPUT =
"Manually define gateway"
56 """Return the gateway which is marked as master."""
57 for hub
in hass.data[DOMAIN].values():
59 return cast(DeconzHub, hub)
64 """Handle a deCONZ config flow."""
68 _hassio_discovery: dict[str, Any]
70 bridges: list[DiscoveredBridge]
78 config_entry: ConfigEntry,
79 ) -> DeconzOptionsFlowHandler:
80 """Get the options flow for this handler."""
84 """Initialize the deCONZ config flow."""
88 self, user_input: dict[str, Any] |
None =
None
89 ) -> ConfigFlowResult:
90 """Handle a deCONZ config flow start.
92 Let user choose between discovered bridges and manual configuration.
93 If no bridge is found allow user to manually input configuration.
95 if user_input
is not None:
96 if user_input[CONF_HOST] == CONF_MANUAL_INPUT:
99 for bridge
in self.
bridgesbridges:
100 if bridge[CONF_HOST] == user_input[CONF_HOST]:
102 self.
hosthost = bridge[CONF_HOST]
103 self.
portport = bridge[CONF_PORT]
106 session = aiohttp_client.async_get_clientsession(self.hass)
109 async
with asyncio.timeout(10):
110 self.
bridgesbridges = await deconz_discovery(session)
112 except (TimeoutError, ResponseError):
115 if LOGGER.isEnabledFor(logging.DEBUG):
116 LOGGER.debug(
"Discovered deCONZ gateways %s", pformat(self.
bridgesbridges))
119 hosts = [bridge[CONF_HOST]
for bridge
in self.
bridgesbridges]
121 hosts.append(CONF_MANUAL_INPUT)
125 data_schema=vol.Schema({vol.Optional(CONF_HOST): vol.In(hosts)}),
131 self, user_input: dict[str, Any] |
None =
None
132 ) -> ConfigFlowResult:
133 """Manual configuration."""
135 self.
hosthost = user_input[CONF_HOST]
136 self.
portport = user_input[CONF_PORT]
140 step_id=
"manual_input",
141 data_schema=vol.Schema(
143 vol.Required(CONF_HOST): str,
144 vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
150 self, user_input: dict[str, Any] |
None =
None
151 ) -> ConfigFlowResult:
152 """Attempt to link with the deCONZ bridge."""
153 errors: dict[str, str] = {}
156 "Preparing linking with deCONZ gateway %s %d", self.
hosthost, self.
portport
159 if user_input
is not None:
160 session = aiohttp_client.async_get_clientsession(self.hass)
161 deconz_session = DeconzSession(session, self.
hosthost, self.
portport)
164 async
with asyncio.timeout(10):
165 api_key = await deconz_session.get_api_key()
167 except LinkButtonNotPressed:
168 errors[
"base"] =
"linking_not_possible"
170 except (ResponseError, RequestError, TimeoutError):
171 errors[
"base"] =
"no_key"
180 """Create entry for gateway."""
182 session = aiohttp_client.async_get_clientsession(self.hass)
185 async
with asyncio.timeout(10):
186 self.
bridge_idbridge_id = await deconz_get_bridge_id(
193 CONF_HOST: self.
hosthost,
194 CONF_PORT: self.
portport,
195 CONF_API_KEY: self.
api_keyapi_key,
205 CONF_HOST: self.
hosthost,
206 CONF_PORT: self.
portport,
207 CONF_API_KEY: self.
api_keyapi_key,
212 self, entry_data: Mapping[str, Any]
213 ) -> ConfigFlowResult:
214 """Trigger a reauthentication flow."""
215 self.context[
"title_placeholders"] = {CONF_HOST: entry_data[CONF_HOST]}
217 self.
hosthost = entry_data[CONF_HOST]
218 self.
portport = entry_data[CONF_PORT]
223 self, discovery_info: ssdp.SsdpServiceInfo
224 ) -> ConfigFlowResult:
225 """Handle a discovered deCONZ bridge."""
226 if LOGGER.isEnabledFor(logging.DEBUG):
227 LOGGER.debug(
"deCONZ SSDP discovery %s", pformat(discovery_info))
229 self.
bridge_idbridge_id = normalize_bridge_id(discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL])
230 parsed_url = urlparse(discovery_info.ssdp_location)
233 if entry
and entry.source == SOURCE_HASSIO:
236 self.
hosthost = cast(str, parsed_url.hostname)
237 self.
portport = cast(int, parsed_url.port)
241 CONF_HOST: self.
hosthost,
242 CONF_PORT: self.
portport,
248 "title_placeholders": {
"host": self.
hosthost},
249 "configuration_url": f
"http://{self.host}:{self.port}",
256 self, discovery_info: HassioServiceInfo
257 ) -> ConfigFlowResult:
258 """Prepare configuration for a Hass.io deCONZ bridge.
260 This flow is triggered by the discovery component.
262 if LOGGER.isEnabledFor(logging.DEBUG):
263 LOGGER.debug(
"deCONZ HASSIO discovery %s", pformat(discovery_info.config))
265 self.
bridge_idbridge_id = normalize_bridge_id(discovery_info.config[CONF_SERIAL])
268 self.
hosthost = discovery_info.config[CONF_HOST]
269 self.
portport = discovery_info.config[CONF_PORT]
270 self.
api_keyapi_key = discovery_info.config[CONF_API_KEY]
274 CONF_HOST: self.
hosthost,
275 CONF_PORT: self.
portport,
276 CONF_API_KEY: self.
api_keyapi_key,
280 self.context[
"configuration_url"] = HASSIO_CONFIGURATION_URL
286 self, user_input: dict[str, Any] |
None =
None
287 ) -> ConfigFlowResult:
288 """Confirm a Hass.io discovery."""
290 if user_input
is not None:
294 step_id=
"hassio_confirm",
295 description_placeholders={
"addon": self.
_hassio_discovery_hassio_discovery[
"addon"]},
300 """Handle deCONZ options."""
305 self, user_input: dict[str, Any] |
None =
None
306 ) -> ConfigFlowResult:
307 """Manage the deCONZ options."""
311 self, user_input: dict[str, Any] |
None =
None
312 ) -> ConfigFlowResult:
313 """Manage the deconz devices options."""
314 if user_input
is not None:
318 for option, default
in (
319 (CONF_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_CLIP_SENSOR),
320 (CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS),
321 (CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES),
331 step_id=
"deconz_devices",
332 data_schema=vol.Schema(schema_options),
ConfigFlowResult async_step_ssdp(self, ssdp.SsdpServiceInfo discovery_info)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_manual_input(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_hassio_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_hassio(self, HassioServiceInfo discovery_info)
ConfigFlowResult _create_entry(self)
DeconzOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_link(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_deconz_devices(self, dict[str, Any]|None user_input=None)
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)
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)
DeconzHub get_master_hub(HomeAssistant hass)
IssData update(pyiss.ISS iss)