1 """Config flow for DLNA DMS."""
3 from __future__
import annotations
6 from pprint
import pformat
7 from typing
import TYPE_CHECKING, Any, cast
8 from urllib.parse
import urlparse
10 from async_upnp_client.profiles.dlna
import DmsDevice
11 import voluptuous
as vol
18 from .const
import CONF_SOURCE_ID, CONFIG_VERSION, DEFAULT_NAME, DOMAIN
19 from .util
import generate_source_id
21 LOGGER = logging.getLogger(__name__)
25 """Handle a DLNA DMS config flow.
27 The Unique Service Name (USN) of the DMS device is used as the unique_id for
28 config entries and for entities. This USN may differ from the root USN if
29 the DMS is an embedded device.
32 VERSION = CONFIG_VERSION
35 """Initialize flow."""
37 self.
_location_location: str |
None =
None
38 self.
_usn_usn: str |
None =
None
39 self.
_name_name: str |
None =
None
42 self, user_input: dict[str, Any] |
None =
None
43 ) -> ConfigFlowResult:
44 """Handle a flow initialized by the user by listing unconfigured devices."""
45 LOGGER.debug(
"async_step_user: user_input: %s", user_input)
47 if user_input
is not None and (host := user_input.get(CONF_HOST)):
58 cast(str, urlparse(discovery.ssdp_location).hostname): discovery
59 for discovery
in discoveries
63 host: f
"{discovery.upnp.get(ssdp.ATTR_UPNP_FRIENDLY_NAME)} ({host})"
64 for host, discovery
in self.
_discoveries_discoveries.items()
66 data_schema = vol.Schema({vol.Optional(CONF_HOST): vol.In(discovery_choices)})
70 self, discovery_info: ssdp.SsdpServiceInfo
71 ) -> ConfigFlowResult:
72 """Handle a flow initialized by SSDP discovery."""
73 if LOGGER.isEnabledFor(logging.DEBUG):
74 LOGGER.debug(
"async_step_ssdp: discovery_info %s", pformat(discovery_info))
79 assert self.
_name_name
is not None
84 discovery_service_list = discovery_info.upnp.get(ssdp.ATTR_UPNP_SERVICE_LIST)
85 if not discovery_service_list:
88 services = discovery_service_list.get(
"service")
90 discovery_service_ids: set[str] = set()
91 elif isinstance(services, list):
92 discovery_service_ids = {service.get(
"serviceId")
for service
in services}
95 discovery_service_ids = {services.get(
"serviceId")}
97 if not DmsDevice.SERVICE_IDS.issubset(discovery_service_ids):
104 self.context[
"title_placeholders"] = {
"name": self.
_name_name}
109 self, user_input: dict[str, Any] |
None =
None
110 ) -> ConfigFlowResult:
111 """Allow the user to confirm adding the device."""
112 if user_input
is not None:
119 """Create a config entry, assuming all required information is now known."""
121 "_create_entry: name: %s, location: %s, USN: %s",
126 assert self.
_name_name
132 CONF_DEVICE_ID: self.
_usn_usn,
138 self, discovery_info: ssdp.SsdpServiceInfo, raise_on_progress: bool =
True
140 """Get required details from an SSDP discovery.
142 Aborts if a device matching the SSDP USN has already been configured.
145 "_async_parse_discovery: location: %s, USN: %s",
146 discovery_info.ssdp_location,
147 discovery_info.ssdp_usn,
150 if not discovery_info.ssdp_location
or not discovery_info.ssdp_usn:
156 self.
_usn_usn = discovery_info.ssdp_usn
161 updates={CONF_URL: self.
_location_location}, reload_on_update=
False
165 discovery_info.upnp.get(ssdp.ATTR_UPNP_FRIENDLY_NAME)
166 or urlparse(self.
_location_location).hostname
171 """Get list of unconfigured DLNA devices discovered by SSDP."""
174 for udn_st
in DmsDevice.DEVICE_TYPES:
175 st_discoveries = await ssdp.async_get_discovery_info_by_st(
178 discoveries.extend(st_discoveries)
181 current_unique_ids = {
185 return [disc
for disc
in discoveries
if disc.ssdp_udn
not in current_unique_ids]
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult _create_entry(self)
list[ssdp.SsdpServiceInfo] _async_get_discoveries(self)
ConfigFlowResult async_step_confirm(self, dict[str, Any]|None user_input=None)
None _async_parse_discovery(self, ssdp.SsdpServiceInfo discovery_info, bool raise_on_progress=True)
ConfigFlowResult async_step_ssdp(self, ssdp.SsdpServiceInfo discovery_info)
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)
list[ConfigEntry] _async_current_entries(self, bool|None include_ignore=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)
_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)
str generate_source_id(HomeAssistant hass, str name)