diff --git a/homeassistant/components/dlna_dmr/config_flow.py b/homeassistant/components/dlna_dmr/config_flow.py index e8999c3ebb61b..ac7e3c8325366 100644 --- a/homeassistant/components/dlna_dmr/config_flow.py +++ b/homeassistant/components/dlna_dmr/config_flow.py @@ -134,10 +134,16 @@ async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowRes discovery_service_list = discovery_info.upnp.get(ssdp.ATTR_UPNP_SERVICE_LIST) if not discovery_service_list: return self.async_abort(reason="not_dmr") - discovery_service_ids = { - service.get("serviceId") - for service in discovery_service_list.get("service") or [] - } + + services = discovery_service_list.get("service") + if not services: + discovery_service_ids: set[str] = set() + elif isinstance(services, list): + discovery_service_ids = {service.get("serviceId") for service in services} + else: + # Only one service defined (etree_to_dict failed to make a list) + discovery_service_ids = {services.get("serviceId")} + if not DmrDevice.SERVICE_IDS.issubset(discovery_service_ids): return self.async_abort(reason="not_dmr") diff --git a/tests/components/dlna_dmr/test_config_flow.py b/tests/components/dlna_dmr/test_config_flow.py index 44ab4ea313fa4..6112c7c5ed5f9 100644 --- a/tests/components/dlna_dmr/test_config_flow.py +++ b/tests/components/dlna_dmr/test_config_flow.py @@ -388,7 +388,7 @@ async def test_ssdp_flow_upnp_udn( async def test_ssdp_missing_services(hass: HomeAssistant) -> None: """Test SSDP ignores devices that are missing required services.""" - # No services defined at all + # No service list at all discovery = dataclasses.replace(MOCK_DISCOVERY) discovery.upnp = discovery.upnp.copy() del discovery.upnp[ssdp.ATTR_UPNP_SERVICE_LIST] @@ -400,6 +400,18 @@ async def test_ssdp_missing_services(hass: HomeAssistant) -> None: assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "not_dmr" + # Service list does not contain services + discovery = dataclasses.replace(MOCK_DISCOVERY) + discovery.upnp = discovery.upnp.copy() + discovery.upnp[ssdp.ATTR_UPNP_SERVICE_LIST] = {"bad_key": "bad_value"} + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=discovery, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "not_dmr" + # AVTransport service is missing discovery = dataclasses.replace(MOCK_DISCOVERY) discovery.upnp = discovery.upnp.copy() @@ -417,6 +429,28 @@ async def test_ssdp_missing_services(hass: HomeAssistant) -> None: assert result["reason"] == "not_dmr" +async def test_ssdp_single_service(hass: HomeAssistant) -> None: + """Test SSDP discovery info with only one service defined. + + THe etree_to_dict function turns multiple services into a list of dicts, but + a single service into only a dict. + """ + discovery = dataclasses.replace(MOCK_DISCOVERY) + discovery.upnp = discovery.upnp.copy() + service_list = discovery.upnp[ssdp.ATTR_UPNP_SERVICE_LIST].copy() + # Turn mock's list of service dicts into a single dict + service_list["service"] = service_list["service"][0] + discovery.upnp[ssdp.ATTR_UPNP_SERVICE_LIST] = service_list + + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=discovery, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "not_dmr" + + async def test_ssdp_ignore_device(hass: HomeAssistant) -> None: """Test SSDP discovery ignores certain devices.""" discovery = dataclasses.replace(MOCK_DISCOVERY)