Retry lifx setup later if device has an unexpected serial (#98783)
This commit is contained in:
parent
426fd62ee3
commit
d0fc0aea40
7 changed files with 52 additions and 23 deletions
|
@ -30,7 +30,7 @@ from .coordinator import LIFXUpdateCoordinator
|
||||||
from .discovery import async_discover_devices, async_trigger_discovery
|
from .discovery import async_discover_devices, async_trigger_discovery
|
||||||
from .manager import LIFXManager
|
from .manager import LIFXManager
|
||||||
from .migration import async_migrate_entities_devices, async_migrate_legacy_entries
|
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_SERVER = "server"
|
||||||
CONF_BROADCAST = "broadcast"
|
CONF_BROADCAST = "broadcast"
|
||||||
|
@ -218,6 +218,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
connection.async_stop()
|
connection.async_stop()
|
||||||
raise
|
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
|
domain_data[entry.entry_id] = coordinator
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -21,7 +21,6 @@ from homeassistant.util import dt as dt_util
|
||||||
from . import (
|
from . import (
|
||||||
DEFAULT_ENTRY_TITLE,
|
DEFAULT_ENTRY_TITLE,
|
||||||
IP_ADDRESS,
|
IP_ADDRESS,
|
||||||
MAC_ADDRESS,
|
|
||||||
SERIAL,
|
SERIAL,
|
||||||
_mocked_clean_bulb,
|
_mocked_clean_bulb,
|
||||||
_patch_config_flow_try_connect,
|
_patch_config_flow_try_connect,
|
||||||
|
@ -38,7 +37,7 @@ async def test_hev_cycle_state(hass: HomeAssistant) -> None:
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_clean_bulb()
|
bulb = _mocked_clean_bulb()
|
||||||
|
|
|
@ -14,7 +14,6 @@ from homeassistant.setup import async_setup_component
|
||||||
from . import (
|
from . import (
|
||||||
DEFAULT_ENTRY_TITLE,
|
DEFAULT_ENTRY_TITLE,
|
||||||
IP_ADDRESS,
|
IP_ADDRESS,
|
||||||
MAC_ADDRESS,
|
|
||||||
SERIAL,
|
SERIAL,
|
||||||
_mocked_bulb,
|
_mocked_bulb,
|
||||||
_patch_config_flow_try_connect,
|
_patch_config_flow_try_connect,
|
||||||
|
@ -38,7 +37,7 @@ async def test_button_restart(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
@ -70,7 +69,7 @@ async def test_button_identify(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
|
|
@ -7,7 +7,7 @@ from homeassistant.setup import async_setup_component
|
||||||
from . import (
|
from . import (
|
||||||
DEFAULT_ENTRY_TITLE,
|
DEFAULT_ENTRY_TITLE,
|
||||||
IP_ADDRESS,
|
IP_ADDRESS,
|
||||||
MAC_ADDRESS,
|
SERIAL,
|
||||||
_mocked_bulb,
|
_mocked_bulb,
|
||||||
_mocked_clean_bulb,
|
_mocked_clean_bulb,
|
||||||
_mocked_infrared_bulb,
|
_mocked_infrared_bulb,
|
||||||
|
@ -30,7 +30,7 @@ async def test_bulb_diagnostics(
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
@ -77,7 +77,7 @@ async def test_clean_bulb_diagnostics(
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_clean_bulb()
|
bulb = _mocked_clean_bulb()
|
||||||
|
@ -129,7 +129,7 @@ async def test_infrared_bulb_diagnostics(
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
@ -177,7 +177,7 @@ async def test_legacy_multizone_bulb_diagnostics(
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_light_strip()
|
bulb = _mocked_light_strip()
|
||||||
|
@ -288,7 +288,7 @@ async def test_multizone_bulb_diagnostics(
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_light_strip()
|
bulb = _mocked_light_strip()
|
||||||
|
|
|
@ -5,6 +5,8 @@ from datetime import timedelta
|
||||||
import socket
|
import socket
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import lifx
|
from homeassistant.components import lifx
|
||||||
from homeassistant.components.lifx import DOMAIN, discovery
|
from homeassistant.components.lifx import DOMAIN, discovery
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
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 async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert already_migrated_config_entry.state == ConfigEntryState.SETUP_RETRY
|
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
|
||||||
|
)
|
||||||
|
|
|
@ -13,7 +13,6 @@ from homeassistant.util import dt as dt_util
|
||||||
from . import (
|
from . import (
|
||||||
DEFAULT_ENTRY_TITLE,
|
DEFAULT_ENTRY_TITLE,
|
||||||
IP_ADDRESS,
|
IP_ADDRESS,
|
||||||
MAC_ADDRESS,
|
|
||||||
SERIAL,
|
SERIAL,
|
||||||
MockLifxCommand,
|
MockLifxCommand,
|
||||||
_mocked_infrared_bulb,
|
_mocked_infrared_bulb,
|
||||||
|
@ -32,7 +31,7 @@ async def test_theme_select(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_light_strip()
|
bulb = _mocked_light_strip()
|
||||||
|
@ -70,7 +69,7 @@ async def test_infrared_brightness(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
@ -100,7 +99,7 @@ async def test_set_infrared_brightness_25_percent(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
@ -139,7 +138,7 @@ async def test_set_infrared_brightness_50_percent(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
@ -178,7 +177,7 @@ async def test_set_infrared_brightness_100_percent(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
@ -217,7 +216,7 @@ async def test_disable_infrared(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
@ -256,7 +255,7 @@ async def test_invalid_infrared_brightness(hass: HomeAssistant) -> None:
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_infrared_bulb()
|
bulb = _mocked_infrared_bulb()
|
||||||
|
|
|
@ -20,7 +20,7 @@ from homeassistant.util import dt as dt_util
|
||||||
from . import (
|
from . import (
|
||||||
DEFAULT_ENTRY_TITLE,
|
DEFAULT_ENTRY_TITLE,
|
||||||
IP_ADDRESS,
|
IP_ADDRESS,
|
||||||
MAC_ADDRESS,
|
SERIAL,
|
||||||
_mocked_bulb,
|
_mocked_bulb,
|
||||||
_mocked_bulb_old_firmware,
|
_mocked_bulb_old_firmware,
|
||||||
_patch_config_flow_try_connect,
|
_patch_config_flow_try_connect,
|
||||||
|
@ -38,7 +38,7 @@ async def test_rssi_sensor(hass: HomeAssistant) -> None:
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
@ -89,7 +89,7 @@ async def test_rssi_sensor_old_firmware(hass: HomeAssistant) -> None:
|
||||||
domain=lifx.DOMAIN,
|
domain=lifx.DOMAIN,
|
||||||
title=DEFAULT_ENTRY_TITLE,
|
title=DEFAULT_ENTRY_TITLE,
|
||||||
data={CONF_HOST: IP_ADDRESS},
|
data={CONF_HOST: IP_ADDRESS},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=SERIAL,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb_old_firmware()
|
bulb = _mocked_bulb_old_firmware()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue