1 """Hyperion config flow."""
3 from __future__
import annotations
6 from collections.abc
import Mapping
7 from contextlib
import suppress
10 from urllib.parse
import urlparse
12 from hyperion
import client, const
13 import voluptuous
as vol
34 from .
import create_hyperion_client
38 CONF_EFFECT_HIDE_LIST,
39 CONF_EFFECT_SHOW_LIST,
46 _LOGGER = logging.getLogger(__name__)
47 _LOGGER.setLevel(logging.DEBUG)
110 """Handle a Hyperion config flow."""
117 """Instantiate config flow."""
118 self.
_data_data: dict[str, Any] = {}
120 self.
_auth_id_auth_id: str |
None =
None
122 self.
_port_ui_port_ui: int = const.DEFAULT_PORT_UI
125 """Create and connect a client instance."""
127 self.
_data_data[CONF_HOST],
128 self.
_data_data[CONF_PORT],
129 token=self.
_data_data.
get(CONF_TOKEN),
130 raw_connection=raw_connection,
134 self, hyperion_client: client.HyperionClient
135 ) -> ConfigFlowResult:
136 """Determine if auth is required."""
137 auth_resp = await hyperion_client.async_is_auth_required()
140 if not auth_resp
or not client.ResponseOK(auth_resp):
142 auth_required = auth_resp.get(const.KEY_INFO, {}).
get(const.KEY_REQUIRED,
False)
148 self, entry_data: Mapping[str, Any]
149 ) -> ConfigFlowResult:
150 """Handle a reauthentication flow."""
152 async
with self.
_create_client_create_client(raw_connection=
True)
as hyperion_client:
153 if not hyperion_client:
158 self, discovery_info: ssdp.SsdpServiceInfo
159 ) -> ConfigFlowResult:
160 """Handle a flow initiated by SSDP."""
196 self.
_data_data[CONF_HOST] = urlparse(discovery_info.ssdp_location).hostname
199 urlparse(discovery_info.ssdp_location).port
or const.DEFAULT_PORT_UI
202 self.
_port_ui_port_ui = const.DEFAULT_PORT_UI
206 discovery_info.upnp.get(
"ports", {}).
get(
207 "jsonServer", const.DEFAULT_PORT_JSON
211 self.
_data_data[CONF_PORT] = const.DEFAULT_PORT_JSON
213 if not (hyperion_id := discovery_info.upnp.get(ssdp.ATTR_UPNP_SERIAL)):
223 async
with self.
_create_client_create_client(raw_connection=
True)
as hyperion_client:
224 if not hyperion_client:
230 user_input: dict[str, Any] |
None =
None,
231 ) -> ConfigFlowResult:
232 """Handle a flow initiated by the user."""
237 async
with self.
_create_client_create_client(raw_connection=
True)
as hyperion_client:
242 errors[CONF_BASE] =
"cannot_connect"
246 data_schema=vol.Schema(
248 vol.Required(CONF_HOST): str,
249 vol.Optional(CONF_PORT, default=const.DEFAULT_PORT_JSON): int,
256 """Cancel the request token task if it exists."""
261 with suppress(asyncio.CancelledError):
266 """Send an async_request_token request."""
267 auth_resp: dict[str, Any] |
None =
None
268 async
with self.
_create_client_create_client(raw_connection=
True)
as hyperion_client:
271 auth_resp = await hyperion_client.async_request_token(
272 comment=DEFAULT_ORIGIN, id=auth_id
274 await self.hass.config_entries.flow.async_configure(
275 flow_id=self.flow_id, user_input=auth_resp
279 """Return the URL of the Hyperion UI."""
286 return f
"http://{self._data[CONF_HOST]}:{self._port_ui}"
289 """Verify login details."""
290 async
with self.
_create_client_create_client(raw_connection=
True)
as hyperion_client:
291 if not hyperion_client:
294 client.LoginResponseOK(
295 await hyperion_client.async_login(token=self.
_data_data[CONF_TOKEN])
301 user_input: dict[str, Any] |
None =
None,
302 ) -> ConfigFlowResult:
303 """Handle the auth step of a flow."""
306 if user_input.get(CONF_CREATE_TOKEN):
310 self.
_data_data[CONF_TOKEN] = user_input.get(CONF_TOKEN)
316 errors[CONF_BASE] =
"invalid_access_token"
320 data_schema=vol.Schema(
322 vol.Required(CONF_CREATE_TOKEN): bool,
323 vol.Optional(CONF_TOKEN): str,
330 self, user_input: dict[str, Any] |
None =
None
331 ) -> ConfigFlowResult:
332 """Send a request for a new token."""
333 if user_input
is None:
334 self.
_auth_id_auth_id = client.generate_random_auth_id()
336 step_id=
"create_token",
337 description_placeholders={
338 CONF_AUTH_ID: self.
_auth_id_auth_id,
347 assert self.
_auth_id_auth_id
is not None
356 self, auth_resp: dict[str, Any] |
None =
None
357 ) -> ConfigFlowResult:
358 """Handle completion of the request for a new token."""
359 if auth_resp
is not None and client.ResponseOK(auth_resp):
360 token = auth_resp.get(const.KEY_INFO, {}).
get(const.KEY_TOKEN)
362 self.
_data_data[CONF_TOKEN] = token
364 next_step_id=
"create_token_success"
369 self, _: dict[str, Any] |
None =
None
370 ) -> ConfigFlowResult:
371 """Create an entry after successful token creation."""
385 self, _: dict[str, Any] |
None =
None
386 ) -> ConfigFlowResult:
387 """Show an error on the auth form."""
393 self, user_input: dict[str, Any] |
None =
None
394 ) -> ConfigFlowResult:
395 """Get final confirmation before entry creation."""
399 description_placeholders={
400 CONF_HOST: self.
_data_data[CONF_HOST],
401 CONF_PORT: self.
_data_data[CONF_PORT],
406 async
with self.
_create_client_create_client()
as hyperion_client:
407 if not hyperion_client:
409 hyperion_id = await hyperion_client.async_sysinfo_id()
414 entry = await self.
async_set_unique_idasync_set_unique_id(hyperion_id, raise_on_progress=
False)
416 if self.context.
get(CONF_SOURCE) == SOURCE_REAUTH
and entry
is not None:
422 title=f
"{self._data[CONF_HOST]}:{self._data[CONF_PORT]}", data=self.
_data_data
428 config_entry: ConfigEntry,
429 ) -> HyperionOptionsFlow:
430 """Get the Hyperion Options flow."""
435 """Hyperion options flow."""
438 """Create and connect a client instance."""
446 self, user_input: dict[str, Any] |
None =
None
447 ) -> ConfigFlowResult:
448 """Manage the options."""
451 async
with self.
_create_client_create_client()
as hyperion_client:
452 if not hyperion_client:
453 return self.
async_abortasync_abort(reason=
"cannot_connect")
454 for effect
in hyperion_client.effects
or []:
455 if const.KEY_NAME
in effect:
456 effects[effect[const.KEY_NAME]] = effect[const.KEY_NAME]
463 if user_input
is not None:
464 effect_show_list = user_input.pop(CONF_EFFECT_SHOW_LIST)
465 user_input[CONF_EFFECT_HIDE_LIST] = sorted(
466 set(effects) - set(effect_show_list)
470 default_effect_show_list =
list(
476 data_schema=vol.Schema(
481 CONF_PRIORITY, DEFAULT_PRIORITY
483 ): vol.All(vol.Coerce(int), vol.Range(min=0, max=255)),
485 CONF_EFFECT_SHOW_LIST,
486 default=default_effect_show_list,
487 ): cv.multi_select(effects),
str _get_hyperion_url(self)
ConfigFlowResult async_step_create_token_success(self, dict[str, Any]|None _=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_create_token_external(self, dict[str, Any]|None auth_resp=None)
ConfigFlowResult async_step_ssdp(self, ssdp.SsdpServiceInfo discovery_info)
HyperionOptionsFlow async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
bool|None _can_login(self)
None _request_token_task_func(self, str auth_id)
ConfigFlowResult _advance_to_auth_step_if_necessary(self, client.HyperionClient hyperion_client)
ConfigFlowResult async_step_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_create_token_fail(self, dict[str, Any]|None _=None)
client.HyperionClient _create_client(self, bool raw_connection=False)
None _cancel_request_token_task(self)
ConfigFlowResult async_step_create_token(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_auth(self, dict[str, Any]|None user_input=None)
client.HyperionClient _create_client(self)
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_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_external_step(self, *str|None step_id=None, str url, Mapping[str, str]|None description_placeholders=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_external_step_done(self, *str next_step_id)
_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)
web.Response get(self, web.Request request, str config_key)
client.HyperionClient create_hyperion_client(*Any args, **Any kwargs)
IssData update(pyiss.ISS iss)