Retry lifx setup later if device has an unexpected serial (#98783)

This commit is contained in:
J. Nick Koston 2023-08-22 10:17:15 -05:00 committed by GitHub
parent 426fd62ee3
commit d0fc0aea40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 23 deletions

View file

@ -30,7 +30,7 @@ from .coordinator import LIFXUpdateCoordinator
from .discovery import async_discover_devices, async_trigger_discovery
from .manager import LIFXManager
from .migration import async_migrate_entities_devices, async_migrate_legacy_entries
from .util import async_entry_is_legacy, async_get_legacy_entry
from .util import async_entry_is_legacy, async_get_legacy_entry, formatted_serial
CONF_SERVER = "server"
CONF_BROADCAST = "broadcast"
@ -218,6 +218,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
connection.async_stop()
raise
serial = formatted_serial(coordinator.serial_number)
if serial != entry.unique_id:
# If the serial number 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 {serial}"
)
domain_data[entry.entry_id] = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View file

@ -21,7 +21,6 @@ from homeassistant.util import dt as dt_util
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
SERIAL,
_mocked_clean_bulb,
_patch_config_flow_try_connect,
@ -38,7 +37,7 @@ async def test_hev_cycle_state(hass: HomeAssistant) -> None:
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_clean_bulb()

View file

@ -14,7 +14,6 @@ from homeassistant.setup import async_setup_component
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
SERIAL,
_mocked_bulb,
_patch_config_flow_try_connect,
@ -38,7 +37,7 @@ async def test_button_restart(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb()
@ -70,7 +69,7 @@ async def test_button_identify(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb()

View file

@ -7,7 +7,7 @@ from homeassistant.setup import async_setup_component
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
SERIAL,
_mocked_bulb,
_mocked_clean_bulb,
_mocked_infrared_bulb,
@ -30,7 +30,7 @@ async def test_bulb_diagnostics(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb()
@ -77,7 +77,7 @@ async def test_clean_bulb_diagnostics(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_clean_bulb()
@ -129,7 +129,7 @@ async def test_infrared_bulb_diagnostics(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
@ -177,7 +177,7 @@ async def test_legacy_multizone_bulb_diagnostics(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_light_strip()
@ -288,7 +288,7 @@ async def test_multizone_bulb_diagnostics(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_light_strip()

View file

@ -5,6 +5,8 @@ from datetime import timedelta
import socket
from unittest.mock import patch
import pytest
from homeassistant.components import lifx
from homeassistant.components.lifx import DOMAIN, discovery
from homeassistant.config_entries import ConfigEntryState
@ -149,3 +151,23 @@ async def test_dns_error_at_startup(hass: HomeAssistant) -> None:
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
assert already_migrated_config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_config_entry_wrong_serial(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test config entry enters setup retry when serial mismatches."""
mismatched_serial = f"{SERIAL[:-1]}0"
already_migrated_config_entry = MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=mismatched_serial
)
already_migrated_config_entry.add_to_hass(hass)
with _patch_discovery(), _patch_config_flow_try_connect(), _patch_device():
await async_setup_component(hass, lifx.DOMAIN, {lifx.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:c0, found aa:bb:cc:dd:ee:cc"
in caplog.text
)

View file

@ -13,7 +13,6 @@ from homeassistant.util import dt as dt_util
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
SERIAL,
MockLifxCommand,
_mocked_infrared_bulb,
@ -32,7 +31,7 @@ async def test_theme_select(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_light_strip()
@ -70,7 +69,7 @@ async def test_infrared_brightness(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
@ -100,7 +99,7 @@ async def test_set_infrared_brightness_25_percent(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
@ -139,7 +138,7 @@ async def test_set_infrared_brightness_50_percent(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
@ -178,7 +177,7 @@ async def test_set_infrared_brightness_100_percent(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
@ -217,7 +216,7 @@ async def test_disable_infrared(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()
@ -256,7 +255,7 @@ async def test_invalid_infrared_brightness(hass: HomeAssistant) -> None:
domain=DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_infrared_bulb()

View file

@ -20,7 +20,7 @@ from homeassistant.util import dt as dt_util
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
SERIAL,
_mocked_bulb,
_mocked_bulb_old_firmware,
_patch_config_flow_try_connect,
@ -38,7 +38,7 @@ async def test_rssi_sensor(hass: HomeAssistant) -> None:
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb()
@ -89,7 +89,7 @@ async def test_rssi_sensor_old_firmware(hass: HomeAssistant) -> None:
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
unique_id=SERIAL,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb_old_firmware()