From 6c2674734ae49e1c74ea7bcdda87d19c9eaf8523 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Mon, 27 Sep 2021 16:39:22 +1000 Subject: [PATCH] SSDP starts config flow only for alive devices (#56551) Co-authored-by: J. Nick Koston --- homeassistant/components/ssdp/__init__.py | 4 ++ tests/components/ssdp/test_init.py | 71 +++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index edb64b780c3..3bfde32e50f 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -405,6 +405,10 @@ class Scanner: ssdp_change = SSDP_SOURCE_SSDP_CHANGE_MAPPING[source] await _async_process_callbacks(callbacks, discovery_info, ssdp_change) + # Config flows should only be created for alive/update messages from alive devices + if ssdp_change == SsdpChange.BYEBYE: + return + for domain in matching_domains: _LOGGER.debug("Discovered %s at %s", domain, location) flow: SSDPFlow = { diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index ef12d2b53f7..86a9b3eea21 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -240,6 +240,77 @@ async def test_scan_not_all_match(mock_get_ssdp, hass, aioclient_mock, mock_flow assert not mock_flow_init.mock_calls +@pytest.mark.usefixtures("mock_get_source_ip") +@patch( + "homeassistant.components.ssdp.async_get_ssdp", + return_value={"mock-domain": [{"deviceType": "Paulus"}]}, +) +async def test_flow_start_only_alive( + mock_get_ssdp, hass, aioclient_mock, mock_flow_init +): + """Test config flow is only started for alive devices.""" + aioclient_mock.get( + "http://1.1.1.1", + text=""" + + + Paulus + + + """, + ) + ssdp_listener = await init_ssdp_component(hass) + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + # Search should start a flow + mock_ssdp_search_response = _ssdp_headers( + { + "st": "mock-st", + "location": "http://1.1.1.1", + "usn": "uuid:mock-udn::mock-st", + } + ) + await ssdp_listener._on_search(mock_ssdp_search_response) + await hass.async_block_till_done() + + mock_flow_init.assert_awaited_once_with( + "mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY + ) + + # ssdp:alive advertisement should start a flow + mock_flow_init.reset_mock() + mock_ssdp_advertisement = _ssdp_headers( + { + "location": "http://1.1.1.1", + "usn": "uuid:mock-udn::mock-st", + "nt": "upnp:rootdevice", + "nts": "ssdp:alive", + } + ) + await ssdp_listener._on_alive(mock_ssdp_advertisement) + await hass.async_block_till_done() + mock_flow_init.assert_awaited_once_with( + "mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY + ) + + # ssdp:byebye advertisement should not start a flow + mock_flow_init.reset_mock() + mock_ssdp_advertisement["nts"] = "ssdp:byebye" + await ssdp_listener._on_byebye(mock_ssdp_advertisement) + await hass.async_block_till_done() + mock_flow_init.assert_not_called() + + # ssdp:update advertisement should start a flow + mock_flow_init.reset_mock() + mock_ssdp_advertisement["nts"] = "ssdp:update" + await ssdp_listener._on_update(mock_ssdp_advertisement) + await hass.async_block_till_done() + mock_flow_init.assert_awaited_once_with( + "mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY + ) + + @patch( # XXX TODO: Isn't this duplicate with mock_get_source_ip? "homeassistant.components.ssdp.Scanner._async_build_source_set", return_value={IPv4Address("192.168.1.1")},