Migrate lutron_caseta to use entry.runtime_data (#121903)

* Migrate lutron_caseta to use entry.runtime_data

* Migrate lutron_caseta to use entry.runtime_data
This commit is contained in:
J. Nick Koston 2024-07-14 16:26:12 -05:00 committed by GitHub
parent 73b836df55
commit 6d8f99903d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 70 additions and 71 deletions

View File

@ -63,6 +63,7 @@ from .models import (
LUTRON_KEYPAD_SERIAL, LUTRON_KEYPAD_SERIAL,
LUTRON_KEYPAD_TYPE, LUTRON_KEYPAD_TYPE,
LutronButton, LutronButton,
LutronCasetaConfigEntry,
LutronCasetaData, LutronCasetaData,
LutronKeypad, LutronKeypad,
LutronKeypadData, LutronKeypadData,
@ -103,8 +104,6 @@ PLATFORMS = [
async def async_setup(hass: HomeAssistant, base_config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, base_config: ConfigType) -> bool:
"""Set up the Lutron component.""" """Set up the Lutron component."""
hass.data.setdefault(DOMAIN, {})
if DOMAIN in base_config: if DOMAIN in base_config:
bridge_configs = base_config[DOMAIN] bridge_configs = base_config[DOMAIN]
for config in bridge_configs: for config in bridge_configs:
@ -126,7 +125,7 @@ async def async_setup(hass: HomeAssistant, base_config: ConfigType) -> bool:
async def _async_migrate_unique_ids( async def _async_migrate_unique_ids(
hass: HomeAssistant, entry: config_entries.ConfigEntry hass: HomeAssistant, entry: LutronCasetaConfigEntry
) -> None: ) -> None:
"""Migrate entities since the occupancygroup were not actually unique.""" """Migrate entities since the occupancygroup were not actually unique."""
@ -153,14 +152,14 @@ async def _async_migrate_unique_ids(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, config_entry: config_entries.ConfigEntry hass: HomeAssistant, entry: LutronCasetaConfigEntry
) -> bool: ) -> bool:
"""Set up a bridge from a config entry.""" """Set up a bridge from a config entry."""
entry_id = config_entry.entry_id entry_id = entry.entry_id
host = config_entry.data[CONF_HOST] host = entry.data[CONF_HOST]
keyfile = hass.config.path(config_entry.data[CONF_KEYFILE]) keyfile = hass.config.path(entry.data[CONF_KEYFILE])
certfile = hass.config.path(config_entry.data[CONF_CERTFILE]) certfile = hass.config.path(entry.data[CONF_CERTFILE])
ca_certs = hass.config.path(config_entry.data[CONF_CA_CERTS]) ca_certs = hass.config.path(entry.data[CONF_CA_CERTS])
bridge = None bridge = None
try: try:
@ -185,14 +184,14 @@ async def async_setup_entry(
raise ConfigEntryNotReady(f"Cannot connect to {host}") raise ConfigEntryNotReady(f"Cannot connect to {host}")
_LOGGER.debug("Connected to Lutron Caseta bridge via LEAP at %s", host) _LOGGER.debug("Connected to Lutron Caseta bridge via LEAP at %s", host)
await _async_migrate_unique_ids(hass, config_entry) await _async_migrate_unique_ids(hass, entry)
bridge_devices = bridge.get_devices() bridge_devices = bridge.get_devices()
bridge_device = bridge_devices[BRIDGE_DEVICE_ID] bridge_device = bridge_devices[BRIDGE_DEVICE_ID]
if not config_entry.unique_id: if not entry.unique_id:
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, unique_id=serial_to_unique_id(bridge_device["serial"]) entry, unique_id=serial_to_unique_id(bridge_device["serial"])
) )
_async_register_bridge_device(hass, entry_id, bridge_device, bridge) _async_register_bridge_device(hass, entry_id, bridge_device, bridge)
@ -202,13 +201,9 @@ async def async_setup_entry(
# Store this bridge (keyed by entry_id) so it can be retrieved by the # Store this bridge (keyed by entry_id) so it can be retrieved by the
# platforms we're setting up. # platforms we're setting up.
hass.data[DOMAIN][entry_id] = LutronCasetaData( entry.runtime_data = LutronCasetaData(bridge, bridge_device, keypad_data)
bridge,
bridge_device,
keypad_data,
)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True
@ -497,14 +492,12 @@ def _async_subscribe_keypad_events(
async def async_unload_entry( async def async_unload_entry(
hass: HomeAssistant, entry: config_entries.ConfigEntry hass: HomeAssistant, entry: LutronCasetaConfigEntry
) -> bool: ) -> bool:
"""Unload the bridge from a config entry.""" """Unload the bridge from a config entry."""
data: LutronCasetaData = hass.data[DOMAIN][entry.entry_id] data = entry.runtime_data
await data.bridge.close() await data.bridge.close()
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
class LutronCasetaDevice(Entity): class LutronCasetaDevice(Entity):
@ -605,10 +598,10 @@ def _id_to_identifier(lutron_id: str) -> tuple[str, str]:
async def async_remove_config_entry_device( async def async_remove_config_entry_device(
hass: HomeAssistant, entry: config_entries.ConfigEntry, device_entry: dr.DeviceEntry hass: HomeAssistant, entry: LutronCasetaConfigEntry, device_entry: dr.DeviceEntry
) -> bool: ) -> bool:
"""Remove lutron_caseta config entry from a device.""" """Remove lutron_caseta config entry from a device."""
data: LutronCasetaData = hass.data[DOMAIN][entry.entry_id] data = entry.runtime_data
bridge = data.bridge bridge = data.bridge
devices = bridge.get_devices() devices = bridge.get_devices()
buttons = bridge.buttons buttons = bridge.buttons

View File

@ -6,7 +6,6 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_SUGGESTED_AREA from homeassistant.const import ATTR_SUGGESTED_AREA
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
@ -14,12 +13,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_name_from_id from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_name_from_id
from .const import CONFIG_URL, MANUFACTURER, UNASSIGNED_AREA from .const import CONFIG_URL, MANUFACTURER, UNASSIGNED_AREA
from .models import LutronCasetaData from .models import LutronCasetaConfigEntry
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: LutronCasetaConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the Lutron Caseta binary_sensor platform. """Set up the Lutron Caseta binary_sensor platform.
@ -27,7 +26,7 @@ async def async_setup_entry(
Adds occupancy groups from the Caseta bridge associated with the Adds occupancy groups from the Caseta bridge associated with the
config_entry as binary_sensor entities. config_entry as binary_sensor entities.
""" """
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
occupancy_groups = bridge.occupancy_groups occupancy_groups = bridge.occupancy_groups
async_add_entities( async_add_entities(

View File

@ -5,24 +5,22 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.components.button import ButtonEntity from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import LutronCasetaDevice from . import LutronCasetaDevice
from .const import DOMAIN as CASETA_DOMAIN
from .device_trigger import LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP from .device_trigger import LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP
from .models import LutronCasetaData from .models import LutronCasetaConfigEntry, LutronCasetaData
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: LutronCasetaConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up Lutron pico and keypad buttons.""" """Set up Lutron pico and keypad buttons."""
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
button_devices = bridge.get_buttons() button_devices = bridge.get_buttons()
all_devices = data.bridge.get_devices() all_devices = data.bridge.get_devices()

View File

@ -10,13 +10,11 @@ from homeassistant.components.cover import (
CoverEntity, CoverEntity,
CoverEntityFeature, CoverEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import LutronCasetaDeviceUpdatableEntity from . import LutronCasetaDeviceUpdatableEntity
from .const import DOMAIN as CASETA_DOMAIN from .models import LutronCasetaConfigEntry
from .models import LutronCasetaData
class LutronCasetaShade(LutronCasetaDeviceUpdatableEntity, CoverEntity): class LutronCasetaShade(LutronCasetaDeviceUpdatableEntity, CoverEntity):
@ -114,7 +112,7 @@ PYLUTRON_TYPE_TO_CLASSES = {
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: LutronCasetaConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the Lutron Caseta cover platform. """Set up the Lutron Caseta cover platform.
@ -122,7 +120,7 @@ async def async_setup_entry(
Adds shades from the Caseta bridge associated with the config_entry as Adds shades from the Caseta bridge associated with the config_entry as
cover entities. cover entities.
""" """
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
cover_devices = bridge.get_devices_by_domain(DOMAIN) cover_devices = bridge.get_devices_by_domain(DOMAIN)
async_add_entities( async_add_entities(

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import cast
import voluptuous as vol import voluptuous as vol
@ -28,7 +29,7 @@ from .const import (
DOMAIN, DOMAIN,
LUTRON_CASETA_BUTTON_EVENT, LUTRON_CASETA_BUTTON_EVENT,
) )
from .models import LutronCasetaData from .models import LutronCasetaConfigEntry
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -434,11 +435,14 @@ async def async_attach_trigger(
def get_lutron_data_by_dr_id(hass: HomeAssistant, device_id: str): def get_lutron_data_by_dr_id(hass: HomeAssistant, device_id: str):
"""Get a lutron integration data for the given device registry device id.""" """Get a lutron integration data for the given device registry device id."""
if DOMAIN not in hass.data: entries = cast(
return None list[LutronCasetaConfigEntry],
hass.config_entries.async_entries(
for entry_id in hass.data[DOMAIN]: DOMAIN, include_ignore=False, include_disabled=False
data: LutronCasetaData = hass.data[DOMAIN][entry_id] ),
if data.keypad_data.dr_device_id_to_keypad.get(device_id): )
return data for entry in entries:
if hasattr(entry, "runtime_data"):
if entry.runtime_data.keypad_data.dr_device_id_to_keypad.get(device_id):
return entry.runtime_data
return None return None

View File

@ -7,15 +7,12 @@ from typing import Any
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import DOMAIN
from .models import LutronCasetaData
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
data: LutronCasetaData = hass.data[DOMAIN][entry.entry_id] data = entry.runtime_data
bridge = data.bridge bridge = data.bridge
return { return {
"entry": { "entry": {

View File

@ -7,7 +7,6 @@ from typing import Any
from pylutron_caseta import FAN_HIGH, FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_OFF from pylutron_caseta import FAN_HIGH, FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_OFF
from homeassistant.components.fan import DOMAIN, FanEntity, FanEntityFeature from homeassistant.components.fan import DOMAIN, FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.percentage import ( from homeassistant.util.percentage import (
@ -16,8 +15,7 @@ from homeassistant.util.percentage import (
) )
from . import LutronCasetaDeviceUpdatableEntity from . import LutronCasetaDeviceUpdatableEntity
from .const import DOMAIN as CASETA_DOMAIN from .models import LutronCasetaConfigEntry
from .models import LutronCasetaData
DEFAULT_ON_PERCENTAGE = 50 DEFAULT_ON_PERCENTAGE = 50
ORDERED_NAMED_FAN_SPEEDS = [FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_HIGH] ORDERED_NAMED_FAN_SPEEDS = [FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_HIGH]
@ -25,7 +23,7 @@ ORDERED_NAMED_FAN_SPEEDS = [FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_HIGH]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: LutronCasetaConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the Lutron Caseta fan platform. """Set up the Lutron Caseta fan platform.
@ -33,7 +31,7 @@ async def async_setup_entry(
Adds fan controllers from the Caseta bridge associated with the config_entry Adds fan controllers from the Caseta bridge associated with the config_entry
as fan entities. as fan entities.
""" """
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
fan_devices = bridge.get_devices_by_domain(DOMAIN) fan_devices = bridge.get_devices_by_domain(DOMAIN)
async_add_entities(LutronCasetaFan(fan_device, data) for fan_device in fan_devices) async_add_entities(LutronCasetaFan(fan_device, data) for fan_device in fan_devices)

View File

@ -25,11 +25,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import LutronCasetaDeviceUpdatableEntity from . import LutronCasetaDeviceUpdatableEntity
from .const import ( from .const import DEVICE_TYPE_SPECTRUM_TUNE, DEVICE_TYPE_WHITE_TUNE
DEVICE_TYPE_SPECTRUM_TUNE,
DEVICE_TYPE_WHITE_TUNE,
DOMAIN as CASETA_DOMAIN,
)
from .models import LutronCasetaData from .models import LutronCasetaData
SUPPORTED_COLOR_MODE_DICT = { SUPPORTED_COLOR_MODE_DICT = {
@ -64,7 +60,7 @@ async def async_setup_entry(
Adds dimmers from the Caseta bridge associated with the config_entry as Adds dimmers from the Caseta bridge associated with the config_entry as
light entities. light entities.
""" """
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
light_devices = bridge.get_devices_by_domain(DOMAIN) light_devices = bridge.get_devices_by_domain(DOMAIN)
async_add_entities( async_add_entities(

View File

@ -8,8 +8,11 @@ from typing import Any, Final, TypedDict
from pylutron_caseta.smartbridge import Smartbridge from pylutron_caseta.smartbridge import Smartbridge
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
type LutronCasetaConfigEntry = ConfigEntry[LutronCasetaData]
@dataclass @dataclass
class LutronCasetaData: class LutronCasetaData:

View File

@ -11,7 +11,6 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN as CASETA_DOMAIN from .const import DOMAIN as CASETA_DOMAIN
from .models import LutronCasetaData
from .util import serial_to_unique_id from .util import serial_to_unique_id
@ -25,7 +24,7 @@ async def async_setup_entry(
Adds scenes from the Caseta bridge associated with the config_entry as Adds scenes from the Caseta bridge associated with the config_entry as
scene entities. scene entities.
""" """
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
scenes = bridge.get_scenes() scenes = bridge.get_scenes()
async_add_entities(LutronCasetaScene(scenes[scene], data) for scene in scenes) async_add_entities(LutronCasetaScene(scenes[scene], data) for scene in scenes)

View File

@ -8,8 +8,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import LutronCasetaDeviceUpdatableEntity from . import LutronCasetaDeviceUpdatableEntity
from .const import DOMAIN as CASETA_DOMAIN
from .models import LutronCasetaData
async def async_setup_entry( async def async_setup_entry(
@ -22,7 +20,7 @@ async def async_setup_entry(
Adds switches from the Caseta bridge associated with the config_entry as Adds switches from the Caseta bridge associated with the config_entry as
switch entities. switch entities.
""" """
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id] data = config_entry.runtime_data
bridge = data.bridge bridge = data.bridge
switch_devices = bridge.get_devices_by_domain(DOMAIN) switch_devices = bridge.get_devices_by_domain(DOMAIN)
async_add_entities( async_add_entities(

View File

@ -125,7 +125,11 @@ async def _async_setup_lutron_with_picos(hass):
async def test_get_triggers(hass: HomeAssistant) -> None: async def test_get_triggers(hass: HomeAssistant) -> None:
"""Test we get the expected triggers from a lutron pico.""" """Test we get the expected triggers from a lutron pico."""
config_entry_id = await _async_setup_lutron_with_picos(hass) config_entry_id = await _async_setup_lutron_with_picos(hass)
data: LutronCasetaData = hass.data[DOMAIN][config_entry_id] # Fetching the config entry runtime_data is a legacy pattern
# and should not be copied for new integrations
data: LutronCasetaData = hass.config_entries.async_get_entry(
config_entry_id
).runtime_data
keypads = data.keypad_data.keypads keypads = data.keypad_data.keypads
device_id = keypads[list(keypads)[0]]["dr_device_id"] device_id = keypads[list(keypads)[0]]["dr_device_id"]
@ -359,7 +363,11 @@ async def test_validate_trigger_config_unknown_device(
"""Test for no press with an unknown device.""" """Test for no press with an unknown device."""
config_entry_id = await _async_setup_lutron_with_picos(hass) config_entry_id = await _async_setup_lutron_with_picos(hass)
data: LutronCasetaData = hass.data[DOMAIN][config_entry_id] # Fetching the config entry runtime_data is a legacy pattern
# and should not be copied for new integrations
data: LutronCasetaData = hass.config_entries.async_get_entry(
config_entry_id
).runtime_data
keypads = data.keypad_data.keypads keypads = data.keypad_data.keypads
lutron_device_id = list(keypads)[0] lutron_device_id = list(keypads)[0]
keypad = keypads[lutron_device_id] keypad = keypads[lutron_device_id]
@ -406,7 +414,11 @@ async def test_validate_trigger_invalid_triggers(
) -> None: ) -> None:
"""Test for click_event with invalid triggers.""" """Test for click_event with invalid triggers."""
config_entry_id = await _async_setup_lutron_with_picos(hass) config_entry_id = await _async_setup_lutron_with_picos(hass)
data: LutronCasetaData = hass.data[DOMAIN][config_entry_id] # Fetching the config entry runtime_data is a legacy pattern
# and should not be copied for new integrations
data: LutronCasetaData = hass.config_entries.async_get_entry(
config_entry_id
).runtime_data
keypads = data.keypad_data.keypads keypads = data.keypad_data.keypads
lutron_device_id = list(keypads)[0] lutron_device_id = list(keypads)[0]
keypad = keypads[lutron_device_id] keypad = keypads[lutron_device_id]

View File

@ -53,7 +53,11 @@ async def test_humanify_lutron_caseta_button_event(hass: HomeAssistant) -> None:
await hass.async_block_till_done() await hass.async_block_till_done()
data: LutronCasetaData = hass.data[DOMAIN][config_entry.entry_id] # Fetching the config entry runtime_data is a legacy pattern
# and should not be copied for new integrations
data: LutronCasetaData = hass.config_entries.async_get_entry(
config_entry.entry_id
).runtime_data
keypads = data.keypad_data.keypads keypads = data.keypad_data.keypads
keypad = keypads["9"] keypad = keypads["9"]
dr_device_id = keypad["dr_device_id"] dr_device_id = keypad["dr_device_id"]