Use runtime_data in hue (#144946)

* Use runtime_data in hue

* More

* Tests
This commit is contained in:
epenet 2025-05-16 14:35:46 +02:00 committed by GitHub
parent 119d0c576a
commit a500eeb831
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 116 additions and 114 deletions

View File

@ -3,17 +3,17 @@
from aiohue.util import normalize_bridge_id from aiohue.util import normalize_bridge_id
from homeassistant.components import persistent_notification from homeassistant.components import persistent_notification
from homeassistant.config_entries import SOURCE_IGNORE, ConfigEntry from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from .bridge import HueBridge from .bridge import HueBridge, HueConfigEntry
from .const import DOMAIN, SERVICE_HUE_ACTIVATE_SCENE from .const import DOMAIN, SERVICE_HUE_ACTIVATE_SCENE
from .migration import check_migration from .migration import check_migration
from .services import async_register_services from .services import async_register_services
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
"""Set up a bridge from a config entry.""" """Set up a bridge from a config entry."""
# check (and run) migrations if needed # check (and run) migrations if needed
await check_migration(hass, entry) await check_migration(hass, entry)
@ -104,10 +104,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
unload_success = await hass.data[DOMAIN][entry.entry_id].async_reset() unload_success = await entry.runtime_data.async_reset()
if len(hass.data[DOMAIN]) == 0: if not hass.config_entries.async_loaded_entries(DOMAIN):
hass.data.pop(DOMAIN)
hass.services.async_remove(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE) hass.services.async_remove(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE)
return unload_success return unload_success

View File

@ -2,23 +2,21 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
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 .bridge import HueBridge from .bridge import HueConfigEntry
from .const import DOMAIN
from .v1.binary_sensor import async_setup_entry as setup_entry_v1 from .v1.binary_sensor import async_setup_entry as setup_entry_v1
from .v2.binary_sensor import async_setup_entry as setup_entry_v2 from .v2.binary_sensor import async_setup_entry as setup_entry_v2
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up binary sensor entities.""" """Set up binary sensor entities."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
await setup_entry_v1(hass, config_entry, async_add_entities) await setup_entry_v1(hass, config_entry, async_add_entities)
else: else:

View File

@ -36,11 +36,13 @@ PLATFORMS_v2 = [
Platform.SWITCH, Platform.SWITCH,
] ]
type HueConfigEntry = ConfigEntry[HueBridge]
class HueBridge: class HueBridge:
"""Manages a single Hue bridge.""" """Manages a single Hue bridge."""
def __init__(self, hass: core.HomeAssistant, config_entry: ConfigEntry) -> None: def __init__(self, hass: core.HomeAssistant, config_entry: HueConfigEntry) -> None:
"""Initialize the system.""" """Initialize the system."""
self.config_entry = config_entry self.config_entry = config_entry
self.hass = hass self.hass = hass
@ -58,7 +60,7 @@ class HueBridge:
else: else:
self.api = HueBridgeV2(self.host, app_key) self.api = HueBridgeV2(self.host, app_key)
# store (this) bridge object in hass data # store (this) bridge object in hass data
hass.data.setdefault(DOMAIN, {})[self.config_entry.entry_id] = self self.config_entry.runtime_data = self
@property @property
def host(self) -> str: def host(self) -> str:
@ -163,7 +165,7 @@ class HueBridge:
) )
if unload_success: if unload_success:
self.hass.data[DOMAIN].pop(self.config_entry.entry_id) delattr(self.config_entry, "runtime_data")
return unload_success return unload_success
@ -179,7 +181,7 @@ class HueBridge:
create_config_flow(self.hass, self.host) create_config_flow(self.hass, self.host)
async def _update_listener(hass: core.HomeAssistant, entry: ConfigEntry) -> None: async def _update_listener(hass: core.HomeAssistant, entry: HueConfigEntry) -> None:
"""Handle ConfigEntry options update.""" """Handle ConfigEntry options update."""
await hass.config_entries.async_reload(entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)

View File

@ -13,12 +13,7 @@ from aiohue.util import normalize_bridge_id
import slugify as unicode_slug import slugify as unicode_slug
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ( from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import CONF_API_KEY, CONF_API_VERSION, CONF_HOST from homeassistant.const import CONF_API_KEY, CONF_API_VERSION, CONF_HOST
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers import ( from homeassistant.helpers import (
@ -28,6 +23,7 @@ from homeassistant.helpers import (
) )
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .bridge import HueConfigEntry
from .const import ( from .const import (
CONF_ALLOW_HUE_GROUPS, CONF_ALLOW_HUE_GROUPS,
CONF_ALLOW_UNREACHABLE, CONF_ALLOW_UNREACHABLE,
@ -53,7 +49,7 @@ class HueFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod @staticmethod
@callback @callback
def async_get_options_flow( def async_get_options_flow(
config_entry: ConfigEntry, config_entry: HueConfigEntry,
) -> HueV1OptionsFlowHandler | HueV2OptionsFlowHandler: ) -> HueV1OptionsFlowHandler | HueV2OptionsFlowHandler:
"""Get the options flow for this handler.""" """Get the options flow for this handler."""
if config_entry.data.get(CONF_API_VERSION, 1) == 1: if config_entry.data.get(CONF_API_VERSION, 1) == 1:

View File

@ -26,14 +26,15 @@ if TYPE_CHECKING:
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from .bridge import HueBridge from .bridge import HueConfigEntry
async def async_validate_trigger_config( async def async_validate_trigger_config(
hass: HomeAssistant, config: ConfigType hass: HomeAssistant, config: ConfigType
) -> ConfigType: ) -> ConfigType:
"""Validate config.""" """Validate config."""
if DOMAIN not in hass.data: entries: list[HueConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN)
if not entries:
# happens at startup # happens at startup
return config return config
device_id = config[CONF_DEVICE_ID] device_id = config[CONF_DEVICE_ID]
@ -42,10 +43,10 @@ async def async_validate_trigger_config(
if (device_entry := dev_reg.async_get(device_id)) is None: if (device_entry := dev_reg.async_get(device_id)) is None:
raise InvalidDeviceAutomationConfig(f"Device ID {device_id} is not valid") raise InvalidDeviceAutomationConfig(f"Device ID {device_id} is not valid")
for conf_entry_id in device_entry.config_entries: for entry in entries:
if conf_entry_id not in hass.data[DOMAIN]: if entry.entry_id not in device_entry.config_entries:
continue continue
bridge: HueBridge = hass.data[DOMAIN][conf_entry_id] bridge = entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
return await async_validate_trigger_config_v1(bridge, device_entry, config) return await async_validate_trigger_config_v1(bridge, device_entry, config)
return await async_validate_trigger_config_v2(bridge, device_entry, config) return await async_validate_trigger_config_v2(bridge, device_entry, config)
@ -65,10 +66,11 @@ async def async_attach_trigger(
if (device_entry := dev_reg.async_get(device_id)) is None: if (device_entry := dev_reg.async_get(device_id)) is None:
raise InvalidDeviceAutomationConfig(f"Device ID {device_id} is not valid") raise InvalidDeviceAutomationConfig(f"Device ID {device_id} is not valid")
for conf_entry_id in device_entry.config_entries: entry: HueConfigEntry
if conf_entry_id not in hass.data[DOMAIN]: for entry in hass.config_entries.async_loaded_entries(DOMAIN):
if entry.entry_id not in device_entry.config_entries:
continue continue
bridge: HueBridge = hass.data[DOMAIN][conf_entry_id] bridge = entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
return await async_attach_trigger_v1( return await async_attach_trigger_v1(
bridge, device_entry, config, action, trigger_info bridge, device_entry, config, action, trigger_info
@ -85,7 +87,8 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str hass: HomeAssistant, device_id: str
) -> list[dict[str, Any]]: ) -> list[dict[str, Any]]:
"""Get device triggers for given (hass) device id.""" """Get device triggers for given (hass) device id."""
if DOMAIN not in hass.data: entries: list[HueConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN)
if not entries:
return [] return []
# lookup device in HASS DeviceRegistry # lookup device in HASS DeviceRegistry
dev_reg: dr.DeviceRegistry = dr.async_get(hass) dev_reg: dr.DeviceRegistry = dr.async_get(hass)
@ -94,10 +97,10 @@ async def async_get_triggers(
# Iterate all config entries for this device # Iterate all config entries for this device
# and work out the bridge version # and work out the bridge version
for conf_entry_id in device_entry.config_entries: for entry in entries:
if conf_entry_id not in hass.data[DOMAIN]: if entry.entry_id not in device_entry.config_entries:
continue continue
bridge: HueBridge = hass.data[DOMAIN][conf_entry_id] bridge = entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
return async_get_triggers_v1(bridge, device_entry) return async_get_triggers_v1(bridge, device_entry)

View File

@ -4,18 +4,16 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .bridge import HueBridge from .bridge import HueConfigEntry
from .const import DOMAIN
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: HueConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
bridge: HueBridge = hass.data[DOMAIN][entry.entry_id] bridge = entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
# diagnostics is only implemented for V2 bridges. # diagnostics is only implemented for V2 bridges.
return {} return {}

View File

@ -14,22 +14,21 @@ from homeassistant.components.event import (
EventEntity, EventEntity,
EventEntityDescription, EventEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
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 .bridge import HueBridge from .bridge import HueConfigEntry
from .const import DEFAULT_BUTTON_EVENT_TYPES, DEVICE_SPECIFIC_EVENT_TYPES, DOMAIN from .const import DEFAULT_BUTTON_EVENT_TYPES, DEVICE_SPECIFIC_EVENT_TYPES
from .v2.entity import HueBaseEntity from .v2.entity import HueBaseEntity
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up event platform from Hue button resources.""" """Set up event platform from Hue button resources."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
if bridge.api_version == 1: if bridge.api_version == 1:

View File

@ -2,12 +2,10 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
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 .bridge import HueBridge from .bridge import HueConfigEntry
from .const import DOMAIN
from .v1.light import async_setup_entry as setup_entry_v1 from .v1.light import async_setup_entry as setup_entry_v1
from .v2.group import async_setup_entry as setup_groups_entry_v2 from .v2.group import async_setup_entry as setup_groups_entry_v2
from .v2.light import async_setup_entry as setup_entry_v2 from .v2.light import async_setup_entry as setup_entry_v2
@ -15,11 +13,11 @@ from .v2.light import async_setup_entry as setup_entry_v2
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up light entities.""" """Set up light entities."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
await setup_entry_v1(hass, config_entry, async_add_entities) await setup_entry_v1(hass, config_entry, async_add_entities)

View File

@ -10,7 +10,6 @@ from aiohue.v2.models.resource import ResourceTypes
from homeassistant import core from homeassistant import core
from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.sensor import SensorDeviceClass from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_API_VERSION, CONF_HOST, CONF_USERNAME from homeassistant.const import CONF_API_KEY, CONF_API_VERSION, CONF_HOST, CONF_USERNAME
from homeassistant.helpers import ( from homeassistant.helpers import (
aiohttp_client, aiohttp_client,
@ -18,12 +17,13 @@ from homeassistant.helpers import (
entity_registry as er, entity_registry as er,
) )
from .bridge import HueConfigEntry
from .const import DOMAIN from .const import DOMAIN
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
async def check_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> None: async def check_migration(hass: core.HomeAssistant, entry: HueConfigEntry) -> None:
"""Check if config entry needs any migration actions.""" """Check if config entry needs any migration actions."""
host = entry.data[CONF_HOST] host = entry.data[CONF_HOST]
@ -66,7 +66,7 @@ async def check_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> None:
hass.config_entries.async_update_entry(entry, data=data) hass.config_entries.async_update_entry(entry, data=data)
async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> None: async def handle_v2_migration(hass: core.HomeAssistant, entry: HueConfigEntry) -> None:
"""Perform migration of devices and entities to V2 Id's.""" """Perform migration of devices and entities to V2 Id's."""
host = entry.data[CONF_HOST] host = entry.data[CONF_HOST]
api_key = entry.data[CONF_API_KEY] api_key = entry.data[CONF_API_KEY]

View File

@ -12,7 +12,6 @@ from aiohue.v2.models.smart_scene import SmartScene as HueSmartScene, SmartScene
import voluptuous as vol import voluptuous as vol
from homeassistant.components.scene import ATTR_TRANSITION, Scene as SceneEntity from homeassistant.components.scene import ATTR_TRANSITION, Scene as SceneEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.entity_platform import (
@ -20,7 +19,7 @@ from homeassistant.helpers.entity_platform import (
async_get_current_platform, async_get_current_platform,
) )
from .bridge import HueBridge from .bridge import HueBridge, HueConfigEntry
from .const import DOMAIN from .const import DOMAIN
from .v2.entity import HueBaseEntity from .v2.entity import HueBaseEntity
from .v2.helpers import normalize_hue_brightness, normalize_hue_transition from .v2.helpers import normalize_hue_brightness, normalize_hue_transition
@ -33,11 +32,11 @@ ATTR_BRIGHTNESS = "brightness"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up scene platform from Hue group scenes.""" """Set up scene platform from Hue group scenes."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
if bridge.api_version == 1: if bridge.api_version == 1:

View File

@ -2,23 +2,21 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
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 .bridge import HueBridge from .bridge import HueConfigEntry
from .const import DOMAIN
from .v1.sensor import async_setup_entry as setup_entry_v1 from .v1.sensor import async_setup_entry as setup_entry_v1
from .v2.sensor import async_setup_entry as setup_entry_v2 from .v2.sensor import async_setup_entry as setup_entry_v2
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up sensor entities.""" """Set up sensor entities."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
if bridge.api_version == 1: if bridge.api_version == 1:
await setup_entry_v1(hass, config_entry, async_add_entities) await setup_entry_v1(hass, config_entry, async_add_entities)
return return

View File

@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.service import verify_domain_control from homeassistant.helpers.service import verify_domain_control
from .bridge import HueBridge from .bridge import HueBridge, HueConfigEntry
from .const import ( from .const import (
ATTR_DYNAMIC, ATTR_DYNAMIC,
ATTR_GROUP_NAME, ATTR_GROUP_NAME,
@ -37,14 +37,16 @@ def async_register_services(hass: HomeAssistant) -> None:
dynamic = call.data.get(ATTR_DYNAMIC, False) dynamic = call.data.get(ATTR_DYNAMIC, False)
# Call the set scene function on each bridge # Call the set scene function on each bridge
entries: list[HueConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN)
tasks = [ tasks = [
hue_activate_scene_v1(bridge, group_name, scene_name, transition) hue_activate_scene_v1(
if bridge.api_version == 1 entry.runtime_data, group_name, scene_name, transition
else hue_activate_scene_v2(
bridge, group_name, scene_name, transition, dynamic
) )
for bridge in hass.data[DOMAIN].values() if entry.runtime_data.api_version == 1
if isinstance(bridge, HueBridge) else hue_activate_scene_v2(
entry.runtime_data, group_name, scene_name, transition, dynamic
)
for entry in entries
] ]
results = await asyncio.gather(*tasks) results = await asyncio.gather(*tasks)

View File

@ -19,23 +19,21 @@ from homeassistant.components.switch import (
SwitchEntity, SwitchEntity,
SwitchEntityDescription, SwitchEntityDescription,
) )
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 .bridge import HueBridge from .bridge import HueConfigEntry
from .const import DOMAIN
from .v2.entity import HueBaseEntity from .v2.entity import HueBaseEntity
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Hue switch platform from Hue resources.""" """Set up Hue switch platform from Hue resources."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
if bridge.api_version == 1: if bridge.api_version == 1:

View File

@ -6,16 +6,22 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
) )
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from ..const import DOMAIN as HUE_DOMAIN from ..bridge import HueConfigEntry
from .sensor_base import SENSOR_CONFIG_MAP, GenericZLLSensor from .sensor_base import SENSOR_CONFIG_MAP, GenericZLLSensor
PRESENCE_NAME_FORMAT = "{} motion" PRESENCE_NAME_FORMAT = "{} motion"
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant,
config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Defer binary sensor setup to the shared sensor module.""" """Defer binary sensor setup to the shared sensor module."""
bridge = hass.data[HUE_DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
if not bridge.sensor_manager: if not bridge.sensor_manager:
return return

View File

@ -27,7 +27,7 @@ from homeassistant.helpers.typing import ConfigType
from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN
if TYPE_CHECKING: if TYPE_CHECKING:
from ..bridge import HueBridge from ..bridge import HueBridge, HueConfigEntry
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
{vol.Required(CONF_TYPE): str, vol.Required(CONF_SUBTYPE): str} {vol.Required(CONF_TYPE): str, vol.Required(CONF_SUBTYPE): str}
@ -111,8 +111,9 @@ REMOTES: dict[str, dict[tuple[str, str], dict[str, int]]] = {
def _get_hue_event_from_device_id(hass, device_id): def _get_hue_event_from_device_id(hass, device_id):
"""Resolve hue event from device id.""" """Resolve hue event from device id."""
for bridge in hass.data.get(DOMAIN, {}).values(): entries: list[HueConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN)
for hue_event in bridge.sensor_manager.current_events.values(): for entry in entries:
for hue_event in entry.runtime_data.sensor_manager.current_events.values():
if device_id == hue_event.device_registry_id: if device_id == hue_event.device_registry_id:
return hue_event return hue_event

View File

@ -28,10 +28,11 @@ from homeassistant.components.light import (
LightEntityFeature, LightEntityFeature,
filter_supported_color_modes, filter_supported_color_modes,
) )
from homeassistant.core import callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import PlatformNotReady from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import (
CoordinatorEntity, CoordinatorEntity,
DataUpdateCoordinator, DataUpdateCoordinator,
@ -39,7 +40,7 @@ from homeassistant.helpers.update_coordinator import (
) )
from homeassistant.util import color as color_util from homeassistant.util import color as color_util
from ..bridge import HueBridge from ..bridge import HueConfigEntry
from ..const import ( from ..const import (
CONF_ALLOW_HUE_GROUPS, CONF_ALLOW_HUE_GROUPS,
CONF_ALLOW_UNREACHABLE, CONF_ALLOW_UNREACHABLE,
@ -139,11 +140,15 @@ def create_light(item_class, coordinator, bridge, is_group, rooms, api, item_id)
) )
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant,
config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Hue lights from a config entry.""" """Set up the Hue lights from a config entry."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api_version = tuple(int(v) for v in bridge.api.config.apiversion.split(".")) api_version = tuple(int(v) for v in bridge.api.config.apiversion.split("."))
rooms = {} rooms: dict[str, str] = {}
allow_groups = config_entry.options.get( allow_groups = config_entry.options.get(
CONF_ALLOW_HUE_GROUPS, DEFAULT_ALLOW_HUE_GROUPS CONF_ALLOW_HUE_GROUPS, DEFAULT_ALLOW_HUE_GROUPS

View File

@ -13,8 +13,10 @@ from homeassistant.components.sensor import (
SensorStateClass, SensorStateClass,
) )
from homeassistant.const import LIGHT_LUX, PERCENTAGE, EntityCategory, UnitOfTemperature from homeassistant.const import LIGHT_LUX, PERCENTAGE, EntityCategory, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from ..const import DOMAIN as HUE_DOMAIN from ..bridge import HueConfigEntry
from .sensor_base import SENSOR_CONFIG_MAP, GenericHueSensor, GenericZLLSensor from .sensor_base import SENSOR_CONFIG_MAP, GenericHueSensor, GenericZLLSensor
LIGHT_LEVEL_NAME_FORMAT = "{} light level" LIGHT_LEVEL_NAME_FORMAT = "{} light level"
@ -22,9 +24,13 @@ REMOTE_NAME_FORMAT = "{} battery level"
TEMPERATURE_NAME_FORMAT = "{} temperature" TEMPERATURE_NAME_FORMAT = "{} temperature"
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant,
config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Defer sensor setup to the shared sensor module.""" """Defer sensor setup to the shared sensor module."""
bridge = hass.data[HUE_DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
if not bridge.sensor_manager: if not bridge.sensor_manager:
return return

View File

@ -27,13 +27,11 @@ 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 ..bridge import HueBridge from ..bridge import HueConfigEntry
from ..const import DOMAIN
from .entity import HueBaseEntity from .entity import HueBaseEntity
type SensorType = CameraMotion | Contact | Motion | EntertainmentConfiguration | Tamper type SensorType = CameraMotion | Contact | Motion | EntertainmentConfiguration | Tamper
@ -48,11 +46,11 @@ type ControllerType = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Hue Sensors from Config Entry.""" """Set up Hue Sensors from Config Entry."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
@callback @callback

View File

@ -22,14 +22,13 @@ from homeassistant.components.light import (
LightEntityDescription, LightEntityDescription,
LightEntityFeature, LightEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import color as color_util from homeassistant.util import color as color_util
from ..bridge import HueBridge from ..bridge import HueBridge, HueConfigEntry
from ..const import DOMAIN from ..const import DOMAIN
from .entity import HueBaseEntity from .entity import HueBaseEntity
from .helpers import ( from .helpers import (
@ -41,11 +40,11 @@ from .helpers import (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Hue groups on light platform.""" """Set up Hue groups on light platform."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
async def async_add_light(event_type: EventType, resource: GroupedLight) -> None: async def async_add_light(event_type: EventType, resource: GroupedLight) -> None:

View File

@ -26,13 +26,12 @@ from homeassistant.components.light import (
LightEntityFeature, LightEntityFeature,
filter_supported_color_modes, filter_supported_color_modes,
) )
from homeassistant.config_entries import ConfigEntry
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 homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.util import color as color_util from homeassistant.util import color as color_util
from ..bridge import HueBridge from ..bridge import HueBridge, HueConfigEntry
from ..const import DOMAIN from ..const import DOMAIN
from .entity import HueBaseEntity from .entity import HueBaseEntity
from .helpers import ( from .helpers import (
@ -51,11 +50,11 @@ DEPRECATED_EFFECT_NONE = "None"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Hue Light from Config Entry.""" """Set up Hue Light from Config Entry."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
controller: LightsController = api.lights controller: LightsController = api.lights
make_light_entity = partial(HueLight, bridge, controller) make_light_entity = partial(HueLight, bridge, controller)

View File

@ -25,13 +25,11 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import LIGHT_LUX, PERCENTAGE, EntityCategory, UnitOfTemperature from homeassistant.const import LIGHT_LUX, PERCENTAGE, EntityCategory, UnitOfTemperature
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 ..bridge import HueBridge from ..bridge import HueBridge, HueConfigEntry
from ..const import DOMAIN
from .entity import HueBaseEntity from .entity import HueBaseEntity
type SensorType = DevicePower | LightLevel | Temperature | ZigbeeConnectivity type SensorType = DevicePower | LightLevel | Temperature | ZigbeeConnectivity
@ -45,11 +43,11 @@ type ControllerType = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: HueConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Hue Sensors from Config Entry.""" """Set up Hue Sensors from Config Entry."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id] bridge = config_entry.runtime_data
api: HueBridgeV2 = bridge.api api: HueBridgeV2 = bridge.api
ctrl_base: SensorsController = api.sensors ctrl_base: SensorsController = api.sensors

View File

@ -59,7 +59,7 @@ def create_mock_bridge(hass: HomeAssistant, api_version: int = 1) -> Mock:
async def async_initialize_bridge(): async def async_initialize_bridge():
if bridge.config_entry: if bridge.config_entry:
hass.data.setdefault(hue.DOMAIN, {})[bridge.config_entry.entry_id] = bridge bridge.config_entry.runtime_data = bridge
if bridge.api_version == 2: if bridge.api_version == 2:
await async_setup_devices(bridge) await async_setup_devices(bridge)
return True return True
@ -73,7 +73,7 @@ def create_mock_bridge(hass: HomeAssistant, api_version: int = 1) -> Mock:
async def async_reset(): async def async_reset():
if bridge.config_entry: if bridge.config_entry:
hass.data[hue.DOMAIN].pop(bridge.config_entry.entry_id) delattr(bridge.config_entry, "runtime_data")
return True return True
bridge.async_reset = async_reset bridge.async_reset = async_reset
@ -273,7 +273,7 @@ async def setup_platform(
api_version=mock_bridge.api_version, host=hostname api_version=mock_bridge.api_version, host=hostname
) )
mock_bridge.config_entry = config_entry mock_bridge.config_entry = config_entry
hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge} config_entry.runtime_data = {config_entry.entry_id: mock_bridge}
# simulate a full setup by manually adding the bridge config entry # simulate a full setup by manually adding the bridge config entry
await setup_bridge(hass, mock_bridge, config_entry) await setup_bridge(hass, mock_bridge, config_entry)

View File

@ -42,7 +42,7 @@ async def test_setup_with_no_config(hass: HomeAssistant) -> None:
assert len(hass.config_entries.flow.async_progress()) == 0 assert len(hass.config_entries.flow.async_progress()) == 0
# No configs stored # No configs stored
assert hue.DOMAIN not in hass.data assert not hass.config_entries.async_entries(hue.DOMAIN)
async def test_unload_entry(hass: HomeAssistant, mock_bridge_setup) -> None: async def test_unload_entry(hass: HomeAssistant, mock_bridge_setup) -> None:
@ -55,15 +55,15 @@ async def test_unload_entry(hass: HomeAssistant, mock_bridge_setup) -> None:
assert await async_setup_component(hass, hue.DOMAIN, {}) is True assert await async_setup_component(hass, hue.DOMAIN, {}) is True
assert len(mock_bridge_setup.mock_calls) == 1 assert len(mock_bridge_setup.mock_calls) == 1
hass.data[hue.DOMAIN] = {entry.entry_id: mock_bridge_setup} entry.runtime_data = mock_bridge_setup
async def mock_reset(): async def mock_reset():
hass.data[hue.DOMAIN].pop(entry.entry_id) delattr(entry, "runtime_data")
return True return True
mock_bridge_setup.async_reset = mock_reset mock_bridge_setup.async_reset = mock_reset
assert await hue.async_unload_entry(hass, entry) assert await hue.async_unload_entry(hass, entry)
assert hue.DOMAIN not in hass.data assert not hasattr(entry, "runtime_data")
async def test_setting_unique_id(hass: HomeAssistant, mock_bridge_setup) -> None: async def test_setting_unique_id(hass: HomeAssistant, mock_bridge_setup) -> None:

View File

@ -185,7 +185,7 @@ async def setup_bridge(hass: HomeAssistant, mock_bridge_v1: Mock) -> None:
) )
config_entry.mock_state(hass, ConfigEntryState.LOADED) config_entry.mock_state(hass, ConfigEntryState.LOADED)
mock_bridge_v1.config_entry = config_entry mock_bridge_v1.config_entry = config_entry
hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge_v1} config_entry.runtime_data = mock_bridge_v1
await hass.config_entries.async_forward_entry_setups(config_entry, ["light"]) await hass.config_entries.async_forward_entry_setups(config_entry, ["light"])
# To flush out the service call to update the group # To flush out the service call to update the group
await hass.async_block_till_done() await hass.async_block_till_done()