mirror of
https://github.com/home-assistant/core.git
synced 2025-07-08 05:47:10 +00:00
Add support for parent_device field so entities are nested within Keypad Devices (#79513)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
618f259fd8
commit
b7e84543c1
@ -177,15 +177,15 @@ async def async_setup_entry(
|
||||
|
||||
buttons = bridge.buttons
|
||||
_async_register_bridge_device(hass, entry_id, bridge_device)
|
||||
button_devices = _async_register_button_devices(
|
||||
hass, entry_id, bridge_device, buttons
|
||||
button_devices, device_info_by_device_id = _async_register_button_devices(
|
||||
hass, entry_id, bridge, bridge_device, buttons
|
||||
)
|
||||
_async_subscribe_pico_remote_events(hass, bridge, buttons)
|
||||
|
||||
# Store this bridge (keyed by entry_id) so it can be retrieved by the
|
||||
# platforms we're setting up.
|
||||
hass.data[DOMAIN][entry_id] = LutronCasetaData(
|
||||
bridge, bridge_device, button_devices
|
||||
bridge, bridge_device, button_devices, device_info_by_device_id
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
@ -213,34 +213,46 @@ def _async_register_bridge_device(
|
||||
def _async_register_button_devices(
|
||||
hass: HomeAssistant,
|
||||
config_entry_id: str,
|
||||
bridge,
|
||||
bridge_device,
|
||||
button_devices_by_id: dict[int, dict],
|
||||
) -> dict[str, dict]:
|
||||
) -> tuple[dict[str, dict], dict[int, dict[str, Any]]]:
|
||||
"""Register button devices (Pico Remotes) in the device registry."""
|
||||
device_registry = dr.async_get(hass)
|
||||
button_devices_by_dr_id: dict[str, dict] = {}
|
||||
seen = set()
|
||||
device_info_by_device_id: dict[int, dict[str, Any]] = {}
|
||||
seen: set[str] = set()
|
||||
bridge_devices = bridge.get_devices()
|
||||
|
||||
for device in button_devices_by_id.values():
|
||||
if "serial" not in device or device["serial"] in seen:
|
||||
|
||||
ha_device = device
|
||||
if "parent_device" in device and device["parent_device"] is not None:
|
||||
# Device is a child of parent_device
|
||||
# use the parent_device for HA device info
|
||||
ha_device = bridge_devices[device["parent_device"]]
|
||||
|
||||
if "serial" not in ha_device or ha_device["serial"] in seen:
|
||||
continue
|
||||
seen.add(device["serial"])
|
||||
area, name = _area_and_name_from_name(device["name"])
|
||||
seen.add(ha_device["serial"])
|
||||
|
||||
area, name = _area_and_name_from_name(ha_device["name"])
|
||||
device_args: dict[str, Any] = {
|
||||
"name": f"{area} {name}",
|
||||
"manufacturer": MANUFACTURER,
|
||||
"config_entry_id": config_entry_id,
|
||||
"identifiers": {(DOMAIN, device["serial"])},
|
||||
"model": f"{device['model']} ({device['type']})",
|
||||
"identifiers": {(DOMAIN, ha_device["serial"])},
|
||||
"model": f"{ha_device['model']} ({ha_device['type']})",
|
||||
"via_device": (DOMAIN, bridge_device["serial"]),
|
||||
}
|
||||
if area != UNASSIGNED_AREA:
|
||||
device_args["suggested_area"] = area
|
||||
|
||||
dr_device = device_registry.async_get_or_create(**device_args)
|
||||
button_devices_by_dr_id[dr_device.id] = device
|
||||
button_devices_by_dr_id[dr_device.id] = ha_device
|
||||
device_info_by_device_id.setdefault(ha_device["device_id"], device_args)
|
||||
|
||||
return button_devices_by_dr_id
|
||||
return button_devices_by_dr_id, device_info_by_device_id
|
||||
|
||||
|
||||
def _area_and_name_from_name(device_name: str) -> tuple[str, str]:
|
||||
@ -282,16 +294,23 @@ def _async_subscribe_pico_remote_events(
|
||||
else:
|
||||
action = ACTION_RELEASE
|
||||
|
||||
type_ = _lutron_model_to_device_type(device["model"], device["type"])
|
||||
area, name = _area_and_name_from_name(device["name"])
|
||||
bridge_devices = bridge_device.get_devices()
|
||||
ha_device = device
|
||||
if "parent_device" in device and device["parent_device"] is not None:
|
||||
# Device is a child of parent_device
|
||||
# use the parent_device for HA device info
|
||||
ha_device = bridge_devices[device["parent_device"]]
|
||||
|
||||
type_ = _lutron_model_to_device_type(ha_device["model"], ha_device["type"])
|
||||
area, name = _area_and_name_from_name(ha_device["name"])
|
||||
leap_button_number = device["button_number"]
|
||||
lip_button_number = async_get_lip_button(type_, leap_button_number)
|
||||
hass_device = dev_reg.async_get_device({(DOMAIN, device["serial"])})
|
||||
hass_device = dev_reg.async_get_device({(DOMAIN, ha_device["serial"])})
|
||||
|
||||
hass.bus.async_fire(
|
||||
LUTRON_CASETA_BUTTON_EVENT,
|
||||
{
|
||||
ATTR_SERIAL: device["serial"],
|
||||
ATTR_SERIAL: ha_device["serial"],
|
||||
ATTR_TYPE: type_,
|
||||
ATTR_BUTTON_NUMBER: lip_button_number,
|
||||
ATTR_LEAP_BUTTON_NUMBER: leap_button_number,
|
||||
@ -327,7 +346,7 @@ class LutronCasetaDevice(Entity):
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, device, bridge, bridge_device):
|
||||
def __init__(self, device, data):
|
||||
"""Set up the base class.
|
||||
|
||||
[:param]device the device metadata
|
||||
@ -335,11 +354,24 @@ class LutronCasetaDevice(Entity):
|
||||
[:param]bridge_device a dict with the details of the bridge
|
||||
"""
|
||||
self._device = device
|
||||
self._smartbridge = bridge
|
||||
self._bridge_device = bridge_device
|
||||
self._bridge_unique_id = serial_to_unique_id(bridge_device["serial"])
|
||||
self._smartbridge = data.bridge
|
||||
self._bridge_device = data.bridge_device
|
||||
self._bridge_unique_id = serial_to_unique_id(data.bridge_device["serial"])
|
||||
if "serial" not in self._device:
|
||||
return
|
||||
|
||||
if "parent_device" in device and (
|
||||
parent_device_info := data.device_info_by_device_id.get(
|
||||
device["parent_device"]
|
||||
)
|
||||
):
|
||||
# Append the child device name to the end of the parent keypad name to create the entity name
|
||||
self._attr_name = f'{parent_device_info["name"]} {device["device_name"]}'
|
||||
# Set the device_info to the same as the Parent Keypad
|
||||
# The entities will be nested inside the keypad device
|
||||
self._attr_device_info = parent_device_info
|
||||
return
|
||||
|
||||
area, name = _area_and_name_from_name(device["name"])
|
||||
self._attr_name = full_name = f"{area} {name}"
|
||||
info = DeviceInfo(
|
||||
|
@ -28,10 +28,9 @@ async def async_setup_entry(
|
||||
"""
|
||||
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
bridge = data.bridge
|
||||
bridge_device = data.bridge_device
|
||||
occupancy_groups = bridge.occupancy_groups
|
||||
async_add_entities(
|
||||
LutronOccupancySensor(occupancy_group, bridge, bridge_device)
|
||||
LutronOccupancySensor(occupancy_group, data)
|
||||
for occupancy_group in occupancy_groups.values()
|
||||
)
|
||||
|
||||
@ -41,9 +40,9 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
|
||||
|
||||
_attr_device_class = BinarySensorDeviceClass.OCCUPANCY
|
||||
|
||||
def __init__(self, device, bridge, bridge_device):
|
||||
def __init__(self, device, data):
|
||||
"""Init an occupancy sensor."""
|
||||
super().__init__(device, bridge, bridge_device)
|
||||
super().__init__(device, data)
|
||||
_, name = _area_and_name_from_name(device["name"])
|
||||
self._attr_name = name
|
||||
self._attr_device_info = DeviceInfo(
|
||||
|
@ -30,11 +30,9 @@ async def async_setup_entry(
|
||||
"""
|
||||
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
bridge = data.bridge
|
||||
bridge_device = data.bridge_device
|
||||
cover_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
async_add_entities(
|
||||
LutronCasetaCover(cover_device, bridge, bridge_device)
|
||||
for cover_device in cover_devices
|
||||
LutronCasetaCover(cover_device, data) for cover_device in cover_devices
|
||||
)
|
||||
|
||||
|
||||
|
@ -34,11 +34,8 @@ async def async_setup_entry(
|
||||
"""
|
||||
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
bridge = data.bridge
|
||||
bridge_device = data.bridge_device
|
||||
fan_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
async_add_entities(
|
||||
LutronCasetaFan(fan_device, bridge, bridge_device) for fan_device in fan_devices
|
||||
)
|
||||
async_add_entities(LutronCasetaFan(fan_device, data) for fan_device in fan_devices)
|
||||
|
||||
|
||||
class LutronCasetaFan(LutronCasetaDeviceUpdatableEntity, FanEntity):
|
||||
|
@ -41,11 +41,9 @@ async def async_setup_entry(
|
||||
"""
|
||||
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
bridge = data.bridge
|
||||
bridge_device = data.bridge_device
|
||||
light_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
async_add_entities(
|
||||
LutronCasetaLight(light_device, bridge, bridge_device)
|
||||
for light_device in light_devices
|
||||
LutronCasetaLight(light_device, data) for light_device in light_devices
|
||||
)
|
||||
|
||||
|
||||
|
@ -14,3 +14,4 @@ class LutronCasetaData:
|
||||
bridge: Smartbridge
|
||||
bridge_device: dict[str, Any]
|
||||
button_devices: dict[str, dict]
|
||||
device_info_by_device_id: dict[int, dict[str, Any]]
|
||||
|
@ -27,23 +27,20 @@ async def async_setup_entry(
|
||||
"""
|
||||
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
bridge = data.bridge
|
||||
bridge_device = data.bridge_device
|
||||
scenes = bridge.get_scenes()
|
||||
async_add_entities(
|
||||
LutronCasetaScene(scenes[scene], bridge, bridge_device) for scene in scenes
|
||||
)
|
||||
async_add_entities(LutronCasetaScene(scenes[scene], data) for scene in scenes)
|
||||
|
||||
|
||||
class LutronCasetaScene(Scene):
|
||||
"""Representation of a Lutron Caseta scene."""
|
||||
|
||||
def __init__(self, scene, bridge, bridge_device):
|
||||
def __init__(self, scene, data):
|
||||
"""Initialize the Lutron Caseta scene."""
|
||||
self._scene_id = scene["scene_id"]
|
||||
self._bridge: Smartbridge = bridge
|
||||
bridge_unique_id = serial_to_unique_id(bridge_device["serial"])
|
||||
self._bridge: Smartbridge = data.bridge
|
||||
bridge_unique_id = serial_to_unique_id(data.bridge_device["serial"])
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(CASETA_DOMAIN, bridge_device["serial"])},
|
||||
identifiers={(CASETA_DOMAIN, data.bridge_device["serial"])},
|
||||
)
|
||||
self._attr_name = _area_and_name_from_name(scene["name"])[1]
|
||||
self._attr_unique_id = f"scene_{bridge_unique_id}_{self._scene_id}"
|
||||
|
@ -24,11 +24,9 @@ async def async_setup_entry(
|
||||
"""
|
||||
data: LutronCasetaData = hass.data[CASETA_DOMAIN][config_entry.entry_id]
|
||||
bridge = data.bridge
|
||||
bridge_device = data.bridge_device
|
||||
switch_devices = bridge.get_devices_by_domain(DOMAIN)
|
||||
async_add_entities(
|
||||
LutronCasetaLight(switch_device, bridge, bridge_device)
|
||||
for switch_device in switch_devices
|
||||
LutronCasetaLight(switch_device, data) for switch_device in switch_devices
|
||||
)
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@ from tests.common import (
|
||||
|
||||
MOCK_BUTTON_DEVICES = [
|
||||
{
|
||||
"device_id": "710",
|
||||
"Name": "Back Hall Pico",
|
||||
"ID": 2,
|
||||
"Area": {"Name": "Back Hall"},
|
||||
@ -50,6 +51,7 @@ MOCK_BUTTON_DEVICES = [
|
||||
"serial": 43845548,
|
||||
},
|
||||
{
|
||||
"device_id": "742",
|
||||
"Name": "Front Steps Sunnata Keypad",
|
||||
"ID": 3,
|
||||
"Area": {"Name": "Front Steps"},
|
||||
@ -87,19 +89,22 @@ async def _async_setup_lutron_with_picos(hass, device_reg):
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
dr_button_devices = {}
|
||||
device_info_by_device_id = {}
|
||||
|
||||
for device in MOCK_BUTTON_DEVICES:
|
||||
dr_device = device_reg.async_get_or_create(
|
||||
name=device["leap_name"],
|
||||
manufacturer=MANUFACTURER,
|
||||
config_entry_id=config_entry.entry_id,
|
||||
identifiers={(DOMAIN, device["serial"])},
|
||||
model=f"{device['model']} ({device[CONF_TYPE]})",
|
||||
)
|
||||
device_args = {
|
||||
"name": device["leap_name"],
|
||||
"manufacturer": MANUFACTURER,
|
||||
"config_entry_id": config_entry.entry_id,
|
||||
"identifiers": {(DOMAIN, device["serial"])},
|
||||
"model": f"{device['model']} ({device[CONF_TYPE]})",
|
||||
}
|
||||
dr_device = device_reg.async_get_or_create(**device_args)
|
||||
dr_button_devices[dr_device.id] = device
|
||||
device_info_by_device_id.setdefault(device["device_id"], device_args)
|
||||
|
||||
hass.data[DOMAIN][config_entry.entry_id] = LutronCasetaData(
|
||||
MagicMock(), MagicMock(), dr_button_devices
|
||||
MagicMock(), MagicMock(), dr_button_devices, device_info_by_device_id
|
||||
)
|
||||
return config_entry.entry_id
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user