Migrate lifx to use runtime_data and HassKey (#147348)

This commit is contained in:
epenet 2025-06-23 20:01:21 +02:00 committed by GitHub
parent a7de947f00
commit 6b242fd277
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 64 additions and 72 deletions

View File

@ -13,7 +13,6 @@ from aiolifx.connection import LIFXConnection
import voluptuous as vol import voluptuous as vol
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_PORT, CONF_PORT,
@ -27,7 +26,7 @@ from homeassistant.helpers.event import async_call_later, async_track_time_inter
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import _LOGGER, DATA_LIFX_MANAGER, DOMAIN, TARGET_ANY from .const import _LOGGER, DATA_LIFX_MANAGER, DOMAIN, TARGET_ANY
from .coordinator import LIFXUpdateCoordinator from .coordinator import LIFXConfigEntry, 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
@ -73,7 +72,7 @@ DISCOVERY_COOLDOWN = 5
async def async_legacy_migration( async def async_legacy_migration(
hass: HomeAssistant, hass: HomeAssistant,
legacy_entry: ConfigEntry, legacy_entry: LIFXConfigEntry,
discovered_devices: Iterable[Light], discovered_devices: Iterable[Light],
) -> bool: ) -> bool:
"""Migrate config entries.""" """Migrate config entries."""
@ -157,7 +156,6 @@ class LIFXDiscoveryManager:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the LIFX component.""" """Set up the LIFX component."""
hass.data[DOMAIN] = {}
migrating = bool(async_get_legacy_entry(hass)) migrating = bool(async_get_legacy_entry(hass))
discovery_manager = LIFXDiscoveryManager(hass, migrating) discovery_manager = LIFXDiscoveryManager(hass, migrating)
@ -187,7 +185,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: LIFXConfigEntry) -> bool:
"""Set up LIFX from a config entry.""" """Set up LIFX from a config entry."""
if async_entry_is_legacy(entry): if async_entry_is_legacy(entry):
return True return True
@ -198,10 +196,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async_migrate_entities_devices(hass, legacy_entry.entry_id, entry) async_migrate_entities_devices(hass, legacy_entry.entry_id, entry)
assert entry.unique_id is not None assert entry.unique_id is not None
domain_data = hass.data[DOMAIN] if DATA_LIFX_MANAGER not in hass.data:
if DATA_LIFX_MANAGER not in domain_data:
manager = LIFXManager(hass) manager = LIFXManager(hass)
domain_data[DATA_LIFX_MANAGER] = manager hass.data[DATA_LIFX_MANAGER] = manager
manager.async_setup() manager.async_setup()
host = entry.data[CONF_HOST] host = entry.data[CONF_HOST]
@ -229,21 +226,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
raise ConfigEntryNotReady( raise ConfigEntryNotReady(
f"Unexpected device found at {host}; expected {entry.unique_id}, found {serial}" f"Unexpected device found at {host}; expected {entry.unique_id}, found {serial}"
) )
domain_data[entry.entry_id] = coordinator entry.runtime_data = 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
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: LIFXConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
if async_entry_is_legacy(entry): if async_entry_is_legacy(entry):
return True return True
domain_data = hass.data[DOMAIN]
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
coordinator: LIFXUpdateCoordinator = domain_data.pop(entry.entry_id) entry.runtime_data.connection.async_stop()
coordinator.connection.async_stop()
# Only the DATA_LIFX_MANAGER left, remove it. # Only the DATA_LIFX_MANAGER left, remove it.
if len(domain_data) == 1: if len(hass.config_entries.async_loaded_entries(DOMAIN)) == 0:
manager: LIFXManager = domain_data.pop(DATA_LIFX_MANAGER) manager = hass.data.pop(DATA_LIFX_MANAGER)
manager.async_unload() manager.async_unload()
return unload_ok return unload_ok

View File

@ -7,13 +7,12 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN, HEV_CYCLE_STATE from .const import HEV_CYCLE_STATE
from .coordinator import LIFXUpdateCoordinator from .coordinator import LIFXConfigEntry, LIFXUpdateCoordinator
from .entity import LIFXEntity from .entity import LIFXEntity
from .util import lifx_features from .util import lifx_features
@ -27,11 +26,11 @@ HEV_CYCLE_STATE_SENSOR = BinarySensorEntityDescription(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: LIFXConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up LIFX from a config entry.""" """Set up LIFX from a config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator = entry.runtime_data
if lifx_features(coordinator.device)["hev"]: if lifx_features(coordinator.device)["hev"]:
async_add_entities( async_add_entities(

View File

@ -7,13 +7,12 @@ from homeassistant.components.button import (
ButtonEntity, ButtonEntity,
ButtonEntityDescription, ButtonEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN, IDENTIFY, RESTART from .const import IDENTIFY, RESTART
from .coordinator import LIFXUpdateCoordinator from .coordinator import LIFXConfigEntry, LIFXUpdateCoordinator
from .entity import LIFXEntity from .entity import LIFXEntity
RESTART_BUTTON_DESCRIPTION = ButtonEntityDescription( RESTART_BUTTON_DESCRIPTION = ButtonEntityDescription(
@ -31,12 +30,11 @@ IDENTIFY_BUTTON_DESCRIPTION = ButtonEntityDescription(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: LIFXConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up LIFX from a config entry.""" """Set up LIFX from a config entry."""
domain_data = hass.data[DOMAIN] coordinator = entry.runtime_data
coordinator: LIFXUpdateCoordinator = domain_data[entry.entry_id]
async_add_entities( async_add_entities(
[LIFXRestartButton(coordinator), LIFXIdentifyButton(coordinator)] [LIFXRestartButton(coordinator), LIFXIdentifyButton(coordinator)]
) )

View File

@ -1,8 +1,17 @@
"""Const for LIFX.""" """Const for LIFX."""
from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING
from homeassistant.util.hass_dict import HassKey
if TYPE_CHECKING:
from .manager import LIFXManager
DOMAIN = "lifx" DOMAIN = "lifx"
DATA_LIFX_MANAGER: HassKey[LIFXManager] = HassKey(DOMAIN)
TARGET_ANY = "00:00:00:00:00:00" TARGET_ANY = "00:00:00:00:00:00"
@ -59,7 +68,6 @@ INFRARED_BRIGHTNESS_VALUES_MAP = {
32767: "50%", 32767: "50%",
65535: "100%", 65535: "100%",
} }
DATA_LIFX_MANAGER = "lifx_manager"
LIFX_CEILING_PRODUCT_IDS = {176, 177, 201, 202} LIFX_CEILING_PRODUCT_IDS = {176, 177, 201, 202}

View File

@ -65,6 +65,8 @@ ZONES_PER_COLOR_UPDATE_REQUEST = 8
RSSI_DBM_FW = AwesomeVersion("2.77") RSSI_DBM_FW = AwesomeVersion("2.77")
type LIFXConfigEntry = ConfigEntry[LIFXUpdateCoordinator]
class FirmwareEffect(IntEnum): class FirmwareEffect(IntEnum):
"""Enumeration of LIFX firmware effects.""" """Enumeration of LIFX firmware effects."""
@ -87,12 +89,12 @@ class SkyType(IntEnum):
class LIFXUpdateCoordinator(DataUpdateCoordinator[None]): class LIFXUpdateCoordinator(DataUpdateCoordinator[None]):
"""DataUpdateCoordinator to gather data for a specific lifx device.""" """DataUpdateCoordinator to gather data for a specific lifx device."""
config_entry: ConfigEntry config_entry: LIFXConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: LIFXConfigEntry,
connection: LIFXConnection, connection: LIFXConnection,
) -> None: ) -> None:
"""Initialize DataUpdateCoordinator.""" """Initialize DataUpdateCoordinator."""

View File

@ -5,21 +5,20 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_IP_ADDRESS, CONF_MAC from homeassistant.const import CONF_HOST, CONF_IP_ADDRESS, CONF_MAC
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import CONF_LABEL, DOMAIN from .const import CONF_LABEL
from .coordinator import LIFXUpdateCoordinator from .coordinator import LIFXConfigEntry
TO_REDACT = [CONF_LABEL, CONF_HOST, CONF_IP_ADDRESS, CONF_MAC] TO_REDACT = [CONF_LABEL, CONF_HOST, CONF_IP_ADDRESS, CONF_MAC]
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: LIFXConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a LIFX config entry.""" """Return diagnostics for a LIFX config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator = entry.runtime_data
return { return {
"entry": { "entry": {
"title": entry.title, "title": entry.title,

View File

@ -17,7 +17,6 @@ from homeassistant.components.light import (
LightEntity, LightEntity,
LightEntityFeature, LightEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -37,7 +36,7 @@ from .const import (
INFRARED_BRIGHTNESS, INFRARED_BRIGHTNESS,
LIFX_CEILING_PRODUCT_IDS, LIFX_CEILING_PRODUCT_IDS,
) )
from .coordinator import FirmwareEffect, LIFXUpdateCoordinator from .coordinator import FirmwareEffect, LIFXConfigEntry, LIFXUpdateCoordinator
from .entity import LIFXEntity from .entity import LIFXEntity
from .manager import ( from .manager import (
SERVICE_EFFECT_COLORLOOP, SERVICE_EFFECT_COLORLOOP,
@ -78,13 +77,12 @@ HSBK_KELVIN = 3
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: LIFXConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up LIFX from a config entry.""" """Set up LIFX from a config entry."""
domain_data = hass.data[DOMAIN] coordinator = entry.runtime_data
coordinator: LIFXUpdateCoordinator = domain_data[entry.entry_id] manager = hass.data[DATA_LIFX_MANAGER]
manager: LIFXManager = domain_data[DATA_LIFX_MANAGER]
device = coordinator.device device = coordinator.device
platform = entity_platform.async_get_current_platform() platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service( platform.async_register_entity_service(
@ -123,7 +121,7 @@ class LIFXLight(LIFXEntity, LightEntity):
self, self,
coordinator: LIFXUpdateCoordinator, coordinator: LIFXUpdateCoordinator,
manager: LIFXManager, manager: LIFXManager,
entry: ConfigEntry, entry: LIFXConfigEntry,
) -> None: ) -> None:
"""Initialize the light.""" """Initialize the light."""
super().__init__(coordinator) super().__init__(coordinator)

View File

@ -30,8 +30,8 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.service import async_extract_referenced_entity_ids from homeassistant.helpers.service import async_extract_referenced_entity_ids
from .const import _ATTR_COLOR_TEMP, ATTR_THEME, DATA_LIFX_MANAGER, DOMAIN from .const import _ATTR_COLOR_TEMP, ATTR_THEME, DOMAIN
from .coordinator import LIFXUpdateCoordinator from .coordinator import LIFXConfigEntry, LIFXUpdateCoordinator
from .util import convert_8_to_16, find_hsbk from .util import convert_8_to_16, find_hsbk
if TYPE_CHECKING: if TYPE_CHECKING:
@ -494,13 +494,11 @@ class LIFXManager:
coordinators: list[LIFXUpdateCoordinator] = [] coordinators: list[LIFXUpdateCoordinator] = []
bulbs: list[Light] = [] bulbs: list[Light] = []
for entry_id, coordinator in self.hass.data[DOMAIN].items(): entry: LIFXConfigEntry
if ( for entry in self.hass.config_entries.async_loaded_entries(DOMAIN):
entry_id != DATA_LIFX_MANAGER if self.entry_id_to_entity_id[entry.entry_id] in entity_ids:
and self.entry_id_to_entity_id[entry_id] in entity_ids coordinators.append(entry.runtime_data)
): bulbs.append(entry.runtime_data.device)
coordinators.append(coordinator)
bulbs.append(coordinator.device)
if start_effect_func := self._effect_dispatch.get(service): if start_effect_func := self._effect_dispatch.get(service):
await start_effect_func(self, bulbs, coordinators, **kwargs) await start_effect_func(self, bulbs, coordinators, **kwargs)

View File

@ -2,11 +2,11 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from .const import _LOGGER, DOMAIN from .const import _LOGGER, DOMAIN
from .coordinator import LIFXConfigEntry
from .discovery import async_init_discovery_flow from .discovery import async_init_discovery_flow
@ -15,7 +15,7 @@ def async_migrate_legacy_entries(
hass: HomeAssistant, hass: HomeAssistant,
discovered_hosts_by_serial: dict[str, str], discovered_hosts_by_serial: dict[str, str],
existing_serials: set[str], existing_serials: set[str],
legacy_entry: ConfigEntry, legacy_entry: LIFXConfigEntry,
) -> int: ) -> int:
"""Migrate the legacy config entries to have an entry per device.""" """Migrate the legacy config entries to have an entry per device."""
_LOGGER.debug( _LOGGER.debug(
@ -45,7 +45,7 @@ def async_migrate_legacy_entries(
@callback @callback
def async_migrate_entities_devices( def async_migrate_entities_devices(
hass: HomeAssistant, legacy_entry_id: str, new_entry: ConfigEntry hass: HomeAssistant, legacy_entry_id: str, new_entry: LIFXConfigEntry
) -> None: ) -> None:
"""Move entities and devices to the new config entry.""" """Move entities and devices to the new config entry."""
migrated_devices = [] migrated_devices = []

View File

@ -5,18 +5,12 @@ from __future__ import annotations
from aiolifx_themes.themes import ThemeLibrary from aiolifx_themes.themes import ThemeLibrary
from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import ( from .const import ATTR_THEME, INFRARED_BRIGHTNESS, INFRARED_BRIGHTNESS_VALUES_MAP
ATTR_THEME, from .coordinator import LIFXConfigEntry, LIFXUpdateCoordinator
DOMAIN,
INFRARED_BRIGHTNESS,
INFRARED_BRIGHTNESS_VALUES_MAP,
)
from .coordinator import LIFXUpdateCoordinator
from .entity import LIFXEntity from .entity import LIFXEntity
from .util import lifx_features from .util import lifx_features
@ -39,11 +33,11 @@ THEME_ENTITY = SelectEntityDescription(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: LIFXConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up LIFX from a config entry.""" """Set up LIFX from a config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator = entry.runtime_data
entities: list[LIFXEntity] = [] entities: list[LIFXEntity] = []

View File

@ -10,13 +10,12 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import ATTR_RSSI, DOMAIN from .const import ATTR_RSSI
from .coordinator import LIFXUpdateCoordinator from .coordinator import LIFXConfigEntry, LIFXUpdateCoordinator
from .entity import LIFXEntity from .entity import LIFXEntity
SCAN_INTERVAL = timedelta(seconds=30) SCAN_INTERVAL = timedelta(seconds=30)
@ -33,11 +32,11 @@ RSSI_SENSOR = SensorEntityDescription(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: LIFXConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up LIFX sensor from config entry.""" """Set up LIFX sensor from config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator = entry.runtime_data
async_add_entities([LIFXRssiSensor(coordinator, RSSI_SENSOR)]) async_add_entities([LIFXRssiSensor(coordinator, RSSI_SENSOR)])

View File

@ -5,7 +5,7 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable from collections.abc import Callable
from functools import partial from functools import partial
from typing import Any from typing import TYPE_CHECKING, Any
from aiolifx import products from aiolifx import products
from aiolifx.aiolifx import Light from aiolifx.aiolifx import Light
@ -21,7 +21,6 @@ from homeassistant.components.light import (
ATTR_RGB_COLOR, ATTR_RGB_COLOR,
ATTR_XY_COLOR, ATTR_XY_COLOR,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.util import color as color_util from homeassistant.util import color as color_util
@ -35,17 +34,20 @@ from .const import (
OVERALL_TIMEOUT, OVERALL_TIMEOUT,
) )
if TYPE_CHECKING:
from .coordinator import LIFXConfigEntry
FIX_MAC_FW = AwesomeVersion("3.70") FIX_MAC_FW = AwesomeVersion("3.70")
@callback @callback
def async_entry_is_legacy(entry: ConfigEntry) -> bool: def async_entry_is_legacy(entry: LIFXConfigEntry) -> bool:
"""Check if a config entry is the legacy shared one.""" """Check if a config entry is the legacy shared one."""
return entry.unique_id is None or entry.unique_id == DOMAIN return entry.unique_id is None or entry.unique_id == DOMAIN
@callback @callback
def async_get_legacy_entry(hass: HomeAssistant) -> ConfigEntry | None: def async_get_legacy_entry(hass: HomeAssistant) -> LIFXConfigEntry | None:
"""Get the legacy config entry.""" """Get the legacy config entry."""
for entry in hass.config_entries.async_entries(DOMAIN): for entry in hass.config_entries.async_entries(DOMAIN):
if async_entry_is_legacy(entry): if async_entry_is_legacy(entry):