1 """Config flow for Logitech Harmony Hub integration."""
3 from __future__
import annotations
8 from urllib.parse
import urlparse
10 from aioharmony.hubconnector_websocket
import HubConnector
12 import voluptuous
as vol
30 from .const
import DOMAIN, PREVIOUS_ACTIVE_ACTIVITY, UNIQUE_ID
32 find_best_name_for_remote,
33 find_unique_id_for_remote,
34 get_harmony_client_if_available,
37 _LOGGER = logging.getLogger(__name__)
39 DATA_SCHEMA = vol.Schema(
40 {vol.Required(CONF_HOST): str, vol.Required(CONF_NAME): str}, extra=vol.ALLOW_EXTRA
45 """Validate the user input allows us to connect.
47 Data has the keys from DATA_SCHEMA with values provided by the user.
55 CONF_HOST: data[CONF_HOST],
61 """Handle a config flow for Logitech Harmony Hub."""
66 """Initialize the Harmony config flow."""
70 self, user_input: dict[str, Any] |
None =
None
71 ) -> ConfigFlowResult:
72 """Handle the initial step."""
73 errors: dict[str, str] = {}
74 if user_input
is not None:
78 errors[
"base"] =
"cannot_connect"
80 _LOGGER.exception(
"Unexpected exception")
81 errors[
"base"] =
"unknown"
83 if "base" not in errors:
92 step_id=
"user", data_schema=DATA_SCHEMA, errors=errors
96 self, discovery_info: ssdp.SsdpServiceInfo
97 ) -> ConfigFlowResult:
98 """Handle a discovered Harmony device."""
99 _LOGGER.debug(
"SSDP discovery_info: %s", discovery_info)
101 parsed_url = urlparse(discovery_info.ssdp_location)
102 friendly_name = discovery_info.upnp[ssdp.ATTR_UPNP_FRIENDLY_NAME]
106 self.context[
"title_placeholders"] = {
"name": friendly_name}
109 CONF_HOST: parsed_url.hostname,
110 CONF_NAME: friendly_name,
113 connector = HubConnector(parsed_url.hostname, asyncio.Queue())
115 remote_id = await connector.get_remote_id()
116 except aiohttp.ClientError:
119 await connector.async_close_session()
121 unique_id =
str(remote_id)
130 self, user_input: dict[str, Any] |
None =
None
131 ) -> ConfigFlowResult:
132 """Attempt to link with the Harmony."""
133 errors: dict[str, str] = {}
135 if user_input
is not None:
146 description_placeholders={
155 config_entry: ConfigEntry,
156 ) -> OptionsFlowHandler:
157 """Get the options flow for this handler."""
161 self, validated: dict[str, Any], user_input: dict[str, Any]
162 ) -> ConfigFlowResult:
163 """Single path to create the config entry from validated input."""
166 CONF_NAME: validated[CONF_NAME],
167 CONF_HOST: validated[CONF_HOST],
177 options: dict[str, Any] = {}
178 if ATTR_ACTIVITY
in user_input:
179 options[ATTR_ACTIVITY] = user_input[ATTR_ACTIVITY]
180 if ATTR_DELAY_SECS
in user_input:
181 options[ATTR_DELAY_SECS] = user_input[ATTR_DELAY_SECS]
186 """Handle a option flow for Harmony."""
189 self, user_input: dict[str, Any] |
None =
None
190 ) -> ConfigFlowResult:
191 """Handle options flow."""
192 if user_input
is not None:
196 data_schema = vol.Schema(
201 ATTR_DELAY_SECS, DEFAULT_DELAY_SECS
203 ): vol.Coerce(float),
207 ATTR_ACTIVITY, PREVIOUS_ACTIVE_ACTIVITY
209 ): vol.In([PREVIOUS_ACTIVE_ACTIVITY, *remote.activity_names]),
212 return self.
async_show_formasync_show_form(step_id=
"init", data_schema=data_schema)
216 """Error to indicate we cannot connect."""
ConfigFlowResult async_step_ssdp(self, ssdp.SsdpServiceInfo discovery_info)
ConfigFlowResult _async_create_entry_from_valid_input(self, dict[str, Any] validated, dict[str, Any] user_input)
ConfigFlowResult async_step_link(self, dict[str, Any]|None user_input=None)
OptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_user(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")
None _set_confirm_only(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)
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)
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)
dict[str, Any] validate_input(dict[str, Any] data)
dict[str, Any] _options_from_user_input(dict[str, Any] user_input)
def find_unique_id_for_remote(HarmonyAPI harmony)
def find_best_name_for_remote(dict data, HarmonyAPI harmony)
HarmonyAPI|None get_harmony_client_if_available(str ip_address)