Reinitialize dhcp discovery flow on config entry removal (#126556)
* Reinitialize dhcp discovery flow on unignore * Tweak * Rediscover on any removed config entry * Adjust log message
This commit is contained in:
parent
b6fe3a3022
commit
972dc89c0f
2 changed files with 375 additions and 19 deletions
|
@ -51,6 +51,7 @@ from homeassistant.helpers import (
|
|||
discovery_flow,
|
||||
)
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac
|
||||
from homeassistant.helpers.discovery_flow import DiscoveryKey
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_state_added_domain,
|
||||
|
@ -155,6 +156,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
await dhcp_watcher.async_start()
|
||||
watchers.append(dhcp_watcher)
|
||||
|
||||
rediscovery_watcher = RediscoveryWatcher(
|
||||
hass, address_data, integration_matchers
|
||||
)
|
||||
rediscovery_watcher.async_start()
|
||||
watchers.append(rediscovery_watcher)
|
||||
|
||||
@callback
|
||||
def _async_stop(event: Event) -> None:
|
||||
for watcher in watchers:
|
||||
|
@ -192,7 +199,11 @@ class WatcherBase:
|
|||
|
||||
@callback
|
||||
def async_process_client(
|
||||
self, ip_address: str, hostname: str, unformatted_mac_address: str
|
||||
self,
|
||||
ip_address: str,
|
||||
hostname: str,
|
||||
unformatted_mac_address: str,
|
||||
force: bool = False,
|
||||
) -> None:
|
||||
"""Process a client."""
|
||||
if (made_ip_address := cached_ip_addresses(ip_address)) is None:
|
||||
|
@ -217,7 +228,8 @@ class WatcherBase:
|
|||
|
||||
data = self._address_data.get(mac_address)
|
||||
if (
|
||||
data
|
||||
not force
|
||||
and data
|
||||
and data[IP_ADDRESS] == compressed_ip_address
|
||||
and data[HOSTNAME].startswith(hostname)
|
||||
):
|
||||
|
@ -271,6 +283,14 @@ class WatcherBase:
|
|||
_LOGGER.debug("Matched %s against %s", data, matcher)
|
||||
matched_domains.add(domain)
|
||||
|
||||
if not matched_domains:
|
||||
return # avoid creating DiscoveryKey if there are no matches
|
||||
|
||||
discovery_key = DiscoveryKey(
|
||||
domain=DOMAIN,
|
||||
key=mac_address,
|
||||
version=1,
|
||||
)
|
||||
for domain in matched_domains:
|
||||
discovery_flow.async_create_flow(
|
||||
self.hass,
|
||||
|
@ -281,6 +301,7 @@ class WatcherBase:
|
|||
hostname=lowercase_hostname,
|
||||
macaddress=mac_address,
|
||||
),
|
||||
discovery_key=discovery_key,
|
||||
)
|
||||
|
||||
|
||||
|
@ -414,6 +435,38 @@ class DHCPWatcher(WatcherBase):
|
|||
self._unsub = await aiodhcpwatcher.async_start(self._async_process_dhcp_request)
|
||||
|
||||
|
||||
class RediscoveryWatcher(WatcherBase):
|
||||
"""Class to trigger rediscovery on config entry removal."""
|
||||
|
||||
@callback
|
||||
def _handle_config_entry_removed(
|
||||
self,
|
||||
entry: config_entries.ConfigEntry,
|
||||
) -> None:
|
||||
"""Handle config entry changes."""
|
||||
for discovery_key in entry.discovery_keys[DOMAIN]:
|
||||
if discovery_key.version != 1 or not isinstance(discovery_key.key, str):
|
||||
continue
|
||||
mac_address = discovery_key.key
|
||||
_LOGGER.debug("Rediscover service %s", mac_address)
|
||||
if data := self._address_data.get(mac_address):
|
||||
self.async_process_client(
|
||||
data[IP_ADDRESS],
|
||||
data[HOSTNAME],
|
||||
mac_address,
|
||||
True, # Force rediscovery
|
||||
)
|
||||
|
||||
@callback
|
||||
def async_start(self) -> None:
|
||||
"""Start watching for config entry removals."""
|
||||
self._unsub = async_dispatcher_connect(
|
||||
self.hass,
|
||||
config_entries.signal_discovered_config_entry_removed(DOMAIN),
|
||||
self._handle_config_entry_removed,
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=4096, typed=True)
|
||||
def _compile_fnmatch(pattern: str) -> re.Pattern:
|
||||
"""Compile a fnmatch pattern."""
|
||||
|
|
|
@ -35,11 +35,17 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.device_registry as dr
|
||||
from homeassistant.helpers.discovery_flow import DiscoveryKey
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
async_fire_time_changed,
|
||||
mock_integration,
|
||||
)
|
||||
|
||||
# connect b8:b7:f1:6d:b5:33 192.168.210.56
|
||||
RAW_DHCP_REQUEST = (
|
||||
|
@ -138,11 +144,15 @@ RAW_DHCP_REQUEST_WITHOUT_HOSTNAME = (
|
|||
|
||||
|
||||
async def _async_get_handle_dhcp_packet(
|
||||
hass: HomeAssistant, integration_matchers: dhcp.DhcpMatchers
|
||||
hass: HomeAssistant,
|
||||
integration_matchers: dhcp.DhcpMatchers,
|
||||
address_data: dict | None = None,
|
||||
) -> Callable[[Any], Awaitable[None]]:
|
||||
if address_data is None:
|
||||
address_data = {}
|
||||
dhcp_watcher = dhcp.DHCPWatcher(
|
||||
hass,
|
||||
{},
|
||||
address_data,
|
||||
integration_matchers,
|
||||
)
|
||||
with patch("aiodhcpwatcher.async_start"):
|
||||
|
@ -177,7 +187,8 @@ async def test_dhcp_match_hostname_and_macaddress(hass: HomeAssistant) -> None:
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -205,7 +216,8 @@ async def test_dhcp_renewal_match_hostname_and_macaddress(hass: HomeAssistant) -
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="50147903852c", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.1.120",
|
||||
|
@ -254,7 +266,8 @@ async def test_registered_devices(
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="50147903852c", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.1.120",
|
||||
|
@ -280,7 +293,8 @@ async def test_dhcp_match_hostname(hass: HomeAssistant) -> None:
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -306,7 +320,8 @@ async def test_dhcp_match_macaddress(hass: HomeAssistant) -> None:
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -335,7 +350,8 @@ async def test_dhcp_multiple_match_only_one_flow(hass: HomeAssistant) -> None:
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -361,7 +377,8 @@ async def test_dhcp_match_macaddress_without_hostname(hass: HomeAssistant) -> No
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="606bbd59e4b4", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.107.151",
|
||||
|
@ -687,7 +704,8 @@ async def test_device_tracker_hostname_and_macaddress_exists_before_start(
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -724,7 +742,8 @@ async def test_device_tracker_registered(hass: HomeAssistant) -> None:
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -803,7 +822,8 @@ async def test_device_tracker_hostname_and_macaddress_after_start(
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -1012,7 +1032,8 @@ async def test_aiodiscover_finds_new_hosts(hass: HomeAssistant) -> None:
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -1074,7 +1095,8 @@ async def test_aiodiscover_does_not_call_again_on_shorter_hostname(
|
|||
assert len(mock_init.mock_calls) == 2
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -1083,7 +1105,8 @@ async def test_aiodiscover_does_not_call_again_on_shorter_hostname(
|
|||
)
|
||||
assert mock_init.mock_calls[1][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[1][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[1][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
|
@ -1140,10 +1163,290 @@ async def test_aiodiscover_finds_new_hosts_after_interval(hass: HomeAssistant) -
|
|||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_DHCP
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
hostname="connect",
|
||||
macaddress="b8b7f16db533",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
),
|
||||
[
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
|
||||
),
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{
|
||||
"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", 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",
|
||||
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
|
||||
async def test_dhcp_rediscover(
|
||||
hass: HomeAssistant,
|
||||
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)
|
||||
|
||||
address_data = {}
|
||||
integration_matchers = dhcp.async_index_integration_matchers(
|
||||
[{"domain": "mock-domain", "hostname": "connect", "macaddress": "B8B7F1*"}]
|
||||
)
|
||||
packet = Ether(RAW_DHCP_REQUEST)
|
||||
|
||||
async_handle_dhcp_packet = await _async_get_handle_dhcp_packet(
|
||||
hass, integration_matchers, address_data
|
||||
)
|
||||
rediscovery_watcher = dhcp.RediscoveryWatcher(
|
||||
hass, address_data, integration_matchers
|
||||
)
|
||||
rediscovery_watcher.async_start()
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await async_handle_dhcp_packet(packet)
|
||||
# Ensure no change is ignored
|
||||
await async_handle_dhcp_packet(packet)
|
||||
|
||||
# Assert the cached MAC address is hexstring without :
|
||||
assert address_data == {
|
||||
"b8b7f16db533": {"hostname": "connect", "ip": "192.168.210.56"}
|
||||
}
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == expected_context
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
hostname="connect",
|
||||
macaddress="b8b7f16db533",
|
||||
)
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_init.mock_calls) == 2
|
||||
assert mock_init.mock_calls[0][1][0] == entry_domain
|
||||
assert mock_init.mock_calls[0][2]["context"] == {"source": "unignore"}
|
||||
assert mock_init.mock_calls[1][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[1][2]["context"] == expected_context
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
),
|
||||
[
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
|
||||
),
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{
|
||||
"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", 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",
|
||||
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"entry_source", [config_entries.SOURCE_USER, config_entries.SOURCE_ZEROCONF]
|
||||
)
|
||||
async def test_dhcp_rediscover_2(
|
||||
hass: HomeAssistant,
|
||||
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)
|
||||
|
||||
address_data = {}
|
||||
integration_matchers = dhcp.async_index_integration_matchers(
|
||||
[{"domain": "mock-domain", "hostname": "connect", "macaddress": "B8B7F1*"}]
|
||||
)
|
||||
packet = Ether(RAW_DHCP_REQUEST)
|
||||
|
||||
async_handle_dhcp_packet = await _async_get_handle_dhcp_packet(
|
||||
hass, integration_matchers, address_data
|
||||
)
|
||||
rediscovery_watcher = dhcp.RediscoveryWatcher(
|
||||
hass, address_data, integration_matchers
|
||||
)
|
||||
rediscovery_watcher.async_start()
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await async_handle_dhcp_packet(packet)
|
||||
# Ensure no change is ignored
|
||||
await async_handle_dhcp_packet(packet)
|
||||
|
||||
# Assert the cached MAC address is hexstring without :
|
||||
assert address_data == {
|
||||
"b8b7f16db533": {"hostname": "connect", "ip": "192.168.210.56"}
|
||||
}
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == expected_context
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
hostname="connect",
|
||||
macaddress="b8b7f16db533",
|
||||
)
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == expected_context
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_async_zeroconf")
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
"entry_source",
|
||||
"entry_unique_id",
|
||||
),
|
||||
[
|
||||
# Discovery key from other domain
|
||||
(
|
||||
"mock-domain",
|
||||
{
|
||||
"bluetooth": (
|
||||
DiscoveryKey(domain="bluetooth", key="b8b7f16db533", version=1),
|
||||
)
|
||||
},
|
||||
config_entries.SOURCE_IGNORE,
|
||||
"mock-unique-id",
|
||||
),
|
||||
# Discovery key from the future
|
||||
(
|
||||
"mock-domain",
|
||||
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=2),)},
|
||||
config_entries.SOURCE_IGNORE,
|
||||
"mock-unique-id",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_dhcp_rediscover_no_match(
|
||||
hass: HomeAssistant,
|
||||
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)
|
||||
|
||||
address_data = {}
|
||||
integration_matchers = dhcp.async_index_integration_matchers(
|
||||
[{"domain": "mock-domain", "hostname": "connect", "macaddress": "B8B7F1*"}]
|
||||
)
|
||||
packet = Ether(RAW_DHCP_REQUEST)
|
||||
|
||||
async_handle_dhcp_packet = await _async_get_handle_dhcp_packet(
|
||||
hass, integration_matchers, address_data
|
||||
)
|
||||
rediscovery_watcher = dhcp.RediscoveryWatcher(
|
||||
hass, address_data, integration_matchers
|
||||
)
|
||||
rediscovery_watcher.async_start()
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await async_handle_dhcp_packet(packet)
|
||||
# Ensure no change is ignored
|
||||
await async_handle_dhcp_packet(packet)
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
|
||||
"source": config_entries.SOURCE_DHCP,
|
||||
}
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == expected_context
|
||||
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
|
||||
ip="192.168.210.56",
|
||||
hostname="connect",
|
||||
macaddress="b8b7f16db533",
|
||||
)
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == entry_domain
|
||||
assert mock_init.mock_calls[0][2]["context"] == {"source": "unignore"}
|
||||
|
|
Loading…
Add table
Reference in a new issue