Reinitialize ssdp discovery flow on unignore (#126557)
This commit is contained in:
parent
4e465a2066
commit
2ee93d974d
2 changed files with 402 additions and 20 deletions
|
@ -12,7 +12,7 @@ from ipaddress import IPv4Address, IPv6Address
|
|||
import logging
|
||||
import socket
|
||||
from time import time
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from urllib.parse import urljoin
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
@ -47,6 +47,7 @@ from homeassistant.core import Event, HassJob, HomeAssistant, callback as core_c
|
|||
from homeassistant.data_entry_flow import BaseServiceInfo
|
||||
from homeassistant.helpers import config_validation as cv, discovery_flow
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
from homeassistant.helpers.instance_id import async_get as async_get_instance_id
|
||||
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
||||
|
@ -394,6 +395,12 @@ class Scanner:
|
|||
self.hass, self.async_scan, SCAN_INTERVAL, name="SSDP scanner"
|
||||
)
|
||||
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
config_entries.signal_discovered_config_entry_removed(DOMAIN),
|
||||
self._handle_config_entry_removed,
|
||||
)
|
||||
|
||||
# Trigger the initial-scan.
|
||||
await self.async_scan()
|
||||
|
||||
|
@ -502,6 +509,7 @@ class Scanner:
|
|||
dst: DeviceOrServiceType,
|
||||
source: SsdpSource,
|
||||
info_desc: Mapping[str, Any],
|
||||
skip_callbacks: bool = False,
|
||||
) -> None:
|
||||
"""Handle a device/service change."""
|
||||
matching_domains: set[str] = set()
|
||||
|
@ -526,7 +534,7 @@ class Scanner:
|
|||
)
|
||||
discovery_info.x_homeassistant_matching_domains = matching_domains
|
||||
|
||||
if callbacks:
|
||||
if callbacks and not skip_callbacks:
|
||||
ssdp_change = SSDP_SOURCE_SSDP_CHANGE_MAPPING[source]
|
||||
_async_process_callbacks(self.hass, callbacks, discovery_info, ssdp_change)
|
||||
|
||||
|
@ -537,14 +545,20 @@ class Scanner:
|
|||
|
||||
_LOGGER.debug("Discovery info: %s", discovery_info)
|
||||
|
||||
location = ssdp_device.location
|
||||
if not matching_domains:
|
||||
return # avoid creating DiscoveryKey if there are no matches
|
||||
|
||||
discovery_key = discovery_flow.DiscoveryKey(
|
||||
domain=DOMAIN, key=ssdp_device.udn, version=1
|
||||
)
|
||||
for domain in matching_domains:
|
||||
_LOGGER.debug("Discovered %s at %s", domain, location)
|
||||
_LOGGER.debug("Discovered %s at %s", domain, ssdp_device.location)
|
||||
discovery_flow.async_create_flow(
|
||||
self.hass,
|
||||
domain,
|
||||
{"source": config_entries.SOURCE_SSDP},
|
||||
discovery_info,
|
||||
discovery_key=discovery_key,
|
||||
)
|
||||
|
||||
def _async_dismiss_discoveries(
|
||||
|
@ -565,14 +579,13 @@ class Scanner:
|
|||
) -> Mapping[str, str]:
|
||||
"""Get description dict."""
|
||||
assert self._description_cache is not None
|
||||
cache = self._description_cache
|
||||
|
||||
has_description, description = self._description_cache.peek_description_dict(
|
||||
location
|
||||
)
|
||||
has_description, description = cache.peek_description_dict(location)
|
||||
if has_description:
|
||||
return description or {}
|
||||
|
||||
return await self._description_cache.async_get_description_dict(location) or {}
|
||||
return await cache.async_get_description_dict(location) or {}
|
||||
|
||||
async def _async_headers_to_discovery_info(
|
||||
self, ssdp_device: SsdpDevice, headers: CaseInsensitiveDict
|
||||
|
@ -581,8 +594,6 @@ class Scanner:
|
|||
|
||||
Building this is a bit expensive so we only do it on demand.
|
||||
"""
|
||||
assert self._description_cache is not None
|
||||
|
||||
location = headers["location"]
|
||||
info_desc = await self._async_get_description_dict(location)
|
||||
return discovery_info_from_headers_and_description(
|
||||
|
@ -618,6 +629,37 @@ class Scanner:
|
|||
if ssdp_device.udn == udn
|
||||
]
|
||||
|
||||
@core_callback
|
||||
def _handle_config_entry_removed(
|
||||
self,
|
||||
entry: config_entries.ConfigEntry,
|
||||
) -> None:
|
||||
"""Handle config entry changes."""
|
||||
if TYPE_CHECKING:
|
||||
assert self._description_cache is not None
|
||||
cache = self._description_cache
|
||||
for discovery_key in entry.discovery_keys[DOMAIN]:
|
||||
if discovery_key.version != 1 or not isinstance(discovery_key.key, str):
|
||||
continue
|
||||
udn = discovery_key.key
|
||||
_LOGGER.debug("Rediscover service %s", udn)
|
||||
|
||||
for ssdp_device in self._ssdp_devices:
|
||||
if ssdp_device.udn != udn:
|
||||
continue
|
||||
for dst in ssdp_device.all_combined_headers:
|
||||
has_cached_desc, info_desc = cache.peek_description_dict(
|
||||
ssdp_device.location
|
||||
)
|
||||
if has_cached_desc and info_desc:
|
||||
self._ssdp_listener_process_callback(
|
||||
ssdp_device,
|
||||
dst,
|
||||
SsdpSource.SEARCH,
|
||||
info_desc,
|
||||
True, # Skip integration callbacks
|
||||
)
|
||||
|
||||
|
||||
def discovery_info_from_headers_and_description(
|
||||
ssdp_device: SsdpDevice,
|
||||
|
|
|
@ -18,10 +18,16 @@ from homeassistant.const import (
|
|||
MATCH_ALL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.discovery_flow import DiscoveryKey
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
async_fire_time_changed,
|
||||
mock_integration,
|
||||
)
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
|
@ -65,7 +71,8 @@ async def test_ssdp_flow_dispatched_on_st(
|
|||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_SSDP
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"]
|
||||
assert mock_call_data.ssdp_st == "mock-st"
|
||||
|
@ -108,7 +115,8 @@ async def test_ssdp_flow_dispatched_on_manufacturer_url(
|
|||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_SSDP
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"]
|
||||
assert mock_call_data.ssdp_st == "mock-st"
|
||||
|
@ -163,7 +171,8 @@ async def test_scan_match_upnp_devicedesc_manufacturer(
|
|||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_SSDP
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
|
||||
|
||||
|
@ -208,7 +217,8 @@ async def test_scan_match_upnp_devicedesc_devicetype(
|
|||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_SSDP
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
|
||||
|
||||
|
@ -339,7 +349,14 @@ async def test_flow_start_only_alive(
|
|||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
mock_flow_init.assert_awaited_once_with(
|
||||
"mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY
|
||||
"mock-domain",
|
||||
context={
|
||||
"discovery_key": DiscoveryKey(
|
||||
domain="ssdp", key="uuid:mock-udn", version=1
|
||||
),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
},
|
||||
data=ANY,
|
||||
)
|
||||
|
||||
# ssdp:alive advertisement should start a flow
|
||||
|
@ -356,7 +373,14 @@ async def test_flow_start_only_alive(
|
|||
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
|
||||
"mock-domain",
|
||||
context={
|
||||
"discovery_key": DiscoveryKey(
|
||||
domain="ssdp", key="uuid:mock-udn", version=1
|
||||
),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
},
|
||||
data=ANY,
|
||||
)
|
||||
|
||||
# ssdp:byebye advertisement should not start a flow
|
||||
|
@ -372,7 +396,14 @@ async def test_flow_start_only_alive(
|
|||
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
|
||||
"mock-domain",
|
||||
context={
|
||||
"discovery_key": DiscoveryKey(
|
||||
domain="ssdp", key="uuid:mock-udn", version=1
|
||||
),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
},
|
||||
data=ANY,
|
||||
)
|
||||
|
||||
|
||||
|
@ -824,7 +855,14 @@ async def test_flow_dismiss_on_byebye(
|
|||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
mock_flow_init.assert_awaited_once_with(
|
||||
"mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY
|
||||
"mock-domain",
|
||||
context={
|
||||
"discovery_key": DiscoveryKey(
|
||||
domain="ssdp", key="uuid:mock-udn", version=1
|
||||
),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
},
|
||||
data=ANY,
|
||||
)
|
||||
|
||||
# ssdp:alive advertisement should start a flow
|
||||
|
@ -841,7 +879,14 @@ async def test_flow_dismiss_on_byebye(
|
|||
ssdp_listener._on_alive(mock_ssdp_advertisement)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
mock_flow_init.assert_awaited_once_with(
|
||||
"mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY
|
||||
"mock-domain",
|
||||
context={
|
||||
"discovery_key": DiscoveryKey(
|
||||
domain="ssdp", key="uuid:mock-udn", version=1
|
||||
),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
},
|
||||
data=ANY,
|
||||
)
|
||||
|
||||
mock_ssdp_advertisement["nts"] = "ssdp:byebye"
|
||||
|
@ -859,3 +904,298 @@ async def test_flow_dismiss_on_byebye(
|
|||
|
||||
assert len(mock_async_progress_by_init_data_type.mock_calls) == 1
|
||||
assert mock_async_abort.mock_calls[0][1][0] == "mock_flow_id"
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.components.ssdp.async_get_ssdp",
|
||||
return_value={"mock-domain": [{"st": "mock-st"}]},
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
),
|
||||
[
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),)},
|
||||
),
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{
|
||||
"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),),
|
||||
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
|
||||
},
|
||||
),
|
||||
# Matching discovery key, other domain
|
||||
# Note: Rediscovery is not currently restricted to the domain of the removed
|
||||
# entry. Such a check can be added if needed.
|
||||
(
|
||||
"comp",
|
||||
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),)},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
|
||||
async def test_ssdp_rediscover(
|
||||
mock_get_ssdp,
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_flow_init,
|
||||
entry_domain: str,
|
||||
entry_discovery_keys: tuple,
|
||||
entry_source: str,
|
||||
) -> None:
|
||||
"""Test we reinitiate flows when an ignored config entry is removed."""
|
||||
entry = MockConfigEntry(
|
||||
domain=entry_domain,
|
||||
discovery_keys=entry_discovery_keys,
|
||||
unique_id="mock-unique-id",
|
||||
state=config_entries.ConfigEntryState.LOADED,
|
||||
source=entry_source,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_ssdp_search_response = _ssdp_headers(
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
"usn": "uuid:mock-udn::mock-st",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
"_source": "search",
|
||||
}
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://1.1.1.1",
|
||||
text="""
|
||||
<root>
|
||||
<device>
|
||||
<deviceType>Paulus</deviceType>
|
||||
<manufacturer>Paulus</manufacturer>
|
||||
</device>
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
ssdp_listener = await init_ssdp_component(hass)
|
||||
ssdp_listener._on_search(mock_ssdp_search_response)
|
||||
await hass.async_block_till_done()
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == expected_context
|
||||
mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"]
|
||||
assert mock_call_data.ssdp_st == "mock-st"
|
||||
assert mock_call_data.ssdp_location == "http://1.1.1.1"
|
||||
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_flow_init.mock_calls) == 3
|
||||
assert mock_flow_init.mock_calls[1][1][0] == entry_domain
|
||||
assert mock_flow_init.mock_calls[1][2]["context"] == {"source": "unignore"}
|
||||
assert mock_flow_init.mock_calls[2][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[2][2]["context"] == expected_context
|
||||
assert (
|
||||
mock_flow_init.mock_calls[2][2]["data"]
|
||||
== mock_flow_init.mock_calls[0][2]["data"]
|
||||
)
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.components.ssdp.async_get_ssdp",
|
||||
return_value={"mock-domain": [{"st": "mock-st"}]},
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
),
|
||||
[
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),)},
|
||||
),
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{
|
||||
"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),),
|
||||
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
|
||||
},
|
||||
),
|
||||
# Matching discovery key, other domain
|
||||
# Note: Rediscovery is not currently restricted to the domain of the removed
|
||||
# entry. Such a check can be added if needed.
|
||||
(
|
||||
"comp",
|
||||
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),)},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"entry_source", [config_entries.SOURCE_USER, config_entries.SOURCE_ZEROCONF]
|
||||
)
|
||||
async def test_ssdp_rediscover_2(
|
||||
mock_get_ssdp,
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_flow_init,
|
||||
entry_domain: str,
|
||||
entry_discovery_keys: tuple,
|
||||
entry_source: str,
|
||||
) -> None:
|
||||
"""Test we reinitiate flows when an ignored config entry is removed.
|
||||
|
||||
This test can be merged with test_zeroconf_rediscover when
|
||||
async_step_unignore has been removed from the ConfigFlow base class.
|
||||
"""
|
||||
entry = MockConfigEntry(
|
||||
domain=entry_domain,
|
||||
discovery_keys=entry_discovery_keys,
|
||||
unique_id="mock-unique-id",
|
||||
state=config_entries.ConfigEntryState.LOADED,
|
||||
source=entry_source,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_ssdp_search_response = _ssdp_headers(
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
"usn": "uuid:mock-udn::mock-st",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
"_source": "search",
|
||||
}
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://1.1.1.1",
|
||||
text="""
|
||||
<root>
|
||||
<device>
|
||||
<deviceType>Paulus</deviceType>
|
||||
<manufacturer>Paulus</manufacturer>
|
||||
</device>
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
ssdp_listener = await init_ssdp_component(hass)
|
||||
ssdp_listener._on_search(mock_ssdp_search_response)
|
||||
await hass.async_block_till_done()
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == expected_context
|
||||
mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"]
|
||||
assert mock_call_data.ssdp_st == "mock-st"
|
||||
assert mock_call_data.ssdp_location == "http://1.1.1.1"
|
||||
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_flow_init.mock_calls) == 2
|
||||
assert mock_flow_init.mock_calls[1][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[1][2]["context"] == expected_context
|
||||
assert (
|
||||
mock_flow_init.mock_calls[1][2]["data"]
|
||||
== mock_flow_init.mock_calls[0][2]["data"]
|
||||
)
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.components.ssdp.async_get_ssdp",
|
||||
return_value={"mock-domain": [{"st": "mock-st"}]},
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
"entry_source",
|
||||
"entry_unique_id",
|
||||
),
|
||||
[
|
||||
# Discovery key from other domain
|
||||
(
|
||||
"mock-domain",
|
||||
{"dhcp": (DiscoveryKey(domain="dhcp", key="uuid:mock-udn", version=1),)},
|
||||
config_entries.SOURCE_IGNORE,
|
||||
"mock-unique-id",
|
||||
),
|
||||
# Discovery key from the future
|
||||
(
|
||||
"mock-domain",
|
||||
{"ssdp": (DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=2),)},
|
||||
config_entries.SOURCE_IGNORE,
|
||||
"mock-unique-id",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_ssdp_rediscover_no_match(
|
||||
mock_get_ssdp,
|
||||
hass: HomeAssistant,
|
||||
mock_flow_init,
|
||||
entry_domain: str,
|
||||
entry_discovery_keys: tuple,
|
||||
entry_source: str,
|
||||
entry_unique_id: str,
|
||||
) -> None:
|
||||
"""Test we don't reinitiate flows when a non matching config entry is removed."""
|
||||
mock_integration(hass, MockModule(entry_domain))
|
||||
entry = MockConfigEntry(
|
||||
domain=entry_domain,
|
||||
discovery_keys=entry_discovery_keys,
|
||||
unique_id=entry_unique_id,
|
||||
state=config_entries.ConfigEntryState.LOADED,
|
||||
source=entry_source,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_ssdp_search_response = _ssdp_headers(
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
"usn": "uuid:mock-udn::mock-st",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
"_source": "search",
|
||||
}
|
||||
)
|
||||
ssdp_listener = await init_ssdp_component(hass)
|
||||
ssdp_listener._on_search(mock_ssdp_search_response)
|
||||
await hass.async_block_till_done()
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="ssdp", key="uuid:mock-udn", version=1),
|
||||
"source": config_entries.SOURCE_SSDP,
|
||||
}
|
||||
assert len(mock_flow_init.mock_calls) == 1
|
||||
assert mock_flow_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_flow_init.mock_calls[0][2]["context"] == expected_context
|
||||
mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"]
|
||||
assert mock_call_data.ssdp_st == "mock-st"
|
||||
assert mock_call_data.ssdp_location == "http://1.1.1.1"
|
||||
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_flow_init.mock_calls) == 2
|
||||
assert mock_flow_init.mock_calls[1][1][0] == entry_domain
|
||||
assert mock_flow_init.mock_calls[1][2]["context"] == {"source": "unignore"}
|
||||
|
|
Loading…
Add table
Reference in a new issue