From cfbfdf7949baabf6f2abe3ee33c7c03c11a34c68 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 17 Jan 2024 17:43:14 -1000 Subject: [PATCH] Fix apple_tv IP Address not being updated from discovery (#107611) --- .../components/apple_tv/config_flow.py | 12 +++++- tests/components/apple_tv/test_config_flow.py | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/apple_tv/config_flow.py b/homeassistant/components/apple_tv/config_flow.py index fc65253fe43..251d1e377d3 100644 --- a/homeassistant/components/apple_tv/config_flow.py +++ b/homeassistant/components/apple_tv/config_flow.py @@ -126,7 +126,7 @@ class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def _entry_unique_id_from_identifers(self, all_identifiers: set[str]) -> str | None: """Search existing entries for an identifier and return the unique id.""" - for entry in self._async_current_entries(): + for entry in self._async_current_entries(include_ignore=True): if not all_identifiers.isdisjoint( entry.data.get(CONF_IDENTIFIERS, [entry.unique_id]) ): @@ -186,7 +186,6 @@ class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if discovery_info.ip_address.version == 6: return self.async_abort(reason="ipv6_not_supported") host = discovery_info.host - self._async_abort_entries_match({CONF_ADDRESS: host}) service_type = discovery_info.type[:-1] # Remove leading . name = discovery_info.name.replace(f".{service_type}.", "") properties = discovery_info.properties @@ -196,6 +195,15 @@ class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if unique_id is None: return self.async_abort(reason="unknown") + # The unique id for the zeroconf service may not be + # the same as the unique id for the device. If the + # device is already configured so if we don't + # find a match here, we will fallback to + # looking up the device by all its identifiers + # in the next block. + await self.async_set_unique_id(unique_id) + self._abort_if_unique_id_configured(updates={CONF_ADDRESS: host}) + if existing_unique_id := self._entry_unique_id_from_identifers({unique_id}): await self.async_set_unique_id(existing_unique_id) self._abort_if_unique_id_configured(updates={CONF_ADDRESS: host}) diff --git a/tests/components/apple_tv/test_config_flow.py b/tests/components/apple_tv/test_config_flow.py index 513c21f7ce5..714fe987bc8 100644 --- a/tests/components/apple_tv/test_config_flow.py +++ b/tests/components/apple_tv/test_config_flow.py @@ -691,6 +691,44 @@ async def test_zeroconf_ip_change(hass: HomeAssistant, mock_scan) -> None: assert unrelated_entry.data[CONF_ADDRESS] == "127.0.0.2" +async def test_zeroconf_ip_change_after_ip_conflict_with_ignored_entry( + hass: HomeAssistant, mock_scan +) -> None: + """Test that the config entry gets updated when the ip changes and reloads.""" + entry = MockConfigEntry( + domain="apple_tv", unique_id="mrpid", data={CONF_ADDRESS: "127.0.0.2"} + ) + ignored_entry = MockConfigEntry( + domain="apple_tv", + unique_id="unrelated", + data={CONF_ADDRESS: "127.0.0.2"}, + source=config_entries.SOURCE_IGNORE, + ) + ignored_entry.add_to_hass(hass) + entry.add_to_hass(hass) + mock_scan.result = [ + create_conf( + IPv4Address("127.0.0.1"), "Device", mrp_service(), airplay_service() + ) + ] + + with patch( + "homeassistant.components.apple_tv.async_setup_entry", return_value=True + ) as mock_async_setup: + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=DMAP_SERVICE, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert len(mock_async_setup.mock_calls) == 1 + assert entry.data[CONF_ADDRESS] == "127.0.0.1" + assert ignored_entry.data[CONF_ADDRESS] == "127.0.0.2" + + async def test_zeroconf_ip_change_via_secondary_identifier( hass: HomeAssistant, mock_scan ) -> None: