From d17ffff3e3524e75fa29ff2efbf26bae05fb7443 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Aug 2023 12:00:39 -0500 Subject: [PATCH] Retry tplink setup later if device has an unexpected mac address (#98784) Retry tplink setup later if device has an unexpected serial If the DHCP reservation changed and there is now a different tplink device at the saved IP address, retry setup later to avoid cross linking devices --- homeassistant/components/tplink/__init__.py | 14 ++++++++++++- tests/components/tplink/test_init.py | 22 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tplink/__init__.py b/homeassistant/components/tplink/__init__.py index 2edea30835f..d8285cbed70 100644 --- a/homeassistant/components/tplink/__init__.py +++ b/homeassistant/components/tplink/__init__.py @@ -85,11 +85,23 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up TPLink from a config entry.""" + host = entry.data[CONF_HOST] try: - device: SmartDevice = await Discover.discover_single(entry.data[CONF_HOST]) + device: SmartDevice = await Discover.discover_single(host) except SmartDeviceException as ex: raise ConfigEntryNotReady from ex + found_mac = dr.format_mac(device.mac) + if found_mac != entry.unique_id: + # If the mac address of the device does not match the unique_id + # of the config entry, it likely means the DHCP lease has expired + # and the device has been assigned a new IP address. We need to + # wait for the next discovery to find the device at its new address + # and update the config entry so we do not mix up devices. + raise ConfigEntryNotReady( + f"Unexpected device found at {host}; expected {entry.unique_id}, found {found_mac}" + ) + hass.data[DOMAIN][entry.entry_id] = TPLinkDataUpdateCoordinator(hass, device) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/tests/components/tplink/test_init.py b/tests/components/tplink/test_init.py index 0354549d451..4206c0de6ad 100644 --- a/tests/components/tplink/test_init.py +++ b/tests/components/tplink/test_init.py @@ -4,6 +4,8 @@ from __future__ import annotations from datetime import timedelta from unittest.mock import MagicMock, patch +import pytest + from homeassistant import setup from homeassistant.components import tplink from homeassistant.components.tplink.const import DOMAIN @@ -111,3 +113,23 @@ async def test_dimmer_switch_unique_id_fix_original_entity_still_exists( ) assert migrated_dimmer_entity_reg.entity_id == original_dimmer_entity_reg.entity_id assert migrated_dimmer_entity_reg.entity_id != rollout_dimmer_entity_reg.entity_id + + +async def test_config_entry_wrong_mac_Address( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test config entry enters setup retry when mac address mismatches.""" + mismatched_mac = f"{MAC_ADDRESS[:-1]}0" + already_migrated_config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=mismatched_mac + ) + already_migrated_config_entry.add_to_hass(hass) + with _patch_discovery(), _patch_single_discovery(): + await async_setup_component(hass, tplink.DOMAIN, {tplink.DOMAIN: {}}) + await hass.async_block_till_done() + assert already_migrated_config_entry.state == ConfigEntryState.SETUP_RETRY + + assert ( + "Unexpected device found at 127.0.0.1; expected aa:bb:cc:dd:ee:f0, found aa:bb:cc:dd:ee:ff" + in caplog.text + )