Add support for area field from pylutron_caseta (#80221)

This commit is contained in:
Kevin Addeman 2022-10-12 20:26:54 -04:00 committed by GitHub
parent 4cf0f9b197
commit 01c66aa7c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 88 additions and 30 deletions

View File

@ -177,7 +177,7 @@ async def async_setup_entry(
) )
buttons = bridge.buttons buttons = bridge.buttons
_async_register_bridge_device(hass, entry_id, bridge_device) _async_register_bridge_device(hass, entry_id, bridge_device, bridge)
button_devices, device_info_by_device_id = _async_register_button_devices( button_devices, device_info_by_device_id = _async_register_button_devices(
hass, entry_id, bridge, bridge_device, buttons hass, entry_id, bridge, bridge_device, buttons
) )
@ -196,18 +196,25 @@ async def async_setup_entry(
@callback @callback
def _async_register_bridge_device( def _async_register_bridge_device(
hass: HomeAssistant, config_entry_id: str, bridge_device: dict hass: HomeAssistant, config_entry_id: str, bridge_device: dict, bridge: Smartbridge
) -> None: ) -> None:
"""Register the bridge device in the device registry.""" """Register the bridge device in the device registry."""
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
name=bridge_device["name"], device_args: DeviceInfo = {
manufacturer=MANUFACTURER, "name": bridge_device["name"],
config_entry_id=config_entry_id, "manufacturer": MANUFACTURER,
identifiers={(DOMAIN, bridge_device["serial"])}, "identifiers": {(DOMAIN, bridge_device["serial"])},
model=f"{bridge_device['model']} ({bridge_device['type']})", "model": f"{bridge_device['model']} ({bridge_device['type']})",
configuration_url="https://device-login.lutron.com", "via_device": (DOMAIN, bridge_device["serial"]),
) "configuration_url": "https://device-login.lutron.com",
}
area = _area_name_from_id(bridge.areas, bridge_device["area"])
if area != UNASSIGNED_AREA:
device_args["suggested_area"] = area
device_registry.async_get_or_create(**device_args, config_entry_id=config_entry_id)
@callback @callback
@ -241,7 +248,10 @@ def _async_register_button_devices(
continue continue
seen.add(ha_device_serial) seen.add(ha_device_serial)
area, name = _area_and_name_from_name(ha_device["name"]) area = _area_name_from_id(bridge.areas, ha_device["area"])
# name field is still a combination of area and name from pylytron-caseta
# extract the name portion only.
name = ha_device["name"].split("_")[-1]
device_args: DeviceInfo = { device_args: DeviceInfo = {
"name": f"{area} {name}", "name": f"{area} {name}",
"manufacturer": MANUFACTURER, "manufacturer": MANUFACTURER,
@ -265,12 +275,19 @@ def _handle_none_keypad_serial(keypad_device: dict, bridge_serial: int) -> str:
return keypad_device["serial"] or f"{bridge_serial}_{keypad_device['device_id']}" return keypad_device["serial"] or f"{bridge_serial}_{keypad_device['device_id']}"
def _area_and_name_from_name(device_name: str) -> tuple[str, str]: def _area_name_from_id(areas: dict[str, dict], area_id: str) -> str:
"""Return the area and name from the devices internal name.""" """Return the full area name including parent(s)."""
if "_" in device_name:
area_device_name = device_name.split("_", 1) if area_id is None:
return area_device_name[0], area_device_name[1] return UNASSIGNED_AREA
return UNASSIGNED_AREA, device_name
area = areas[area_id]
if "parent_id" in area:
parent_area = area["parent_id"]
if parent_area is not None:
return f"{_area_name_from_id(areas, parent_area)} {area['name']}"
return area["name"]
@callback @callback
@ -316,7 +333,8 @@ def _async_subscribe_pico_remote_events(
) )
type_ = _lutron_model_to_device_type(ha_device["model"], ha_device["type"]) type_ = _lutron_model_to_device_type(ha_device["model"], ha_device["type"])
area, name = _area_and_name_from_name(ha_device["name"]) area = _area_name_from_id(bridge_device.areas, ha_device["area"])
name = ha_device["name"].split("_")[-1]
leap_button_number = device["button_number"] leap_button_number = device["button_number"]
lip_button_number = async_get_lip_button(type_, leap_button_number) lip_button_number = async_get_lip_button(type_, leap_button_number)
hass_device = dev_reg.async_get_device({(DOMAIN, ha_device_serial)}) hass_device = dev_reg.async_get_device({(DOMAIN, ha_device_serial)})
@ -377,8 +395,8 @@ class LutronCasetaDevice(Entity):
if "parent_device" in device: if "parent_device" in device:
# This is a child entity, handle the naming in button.py and switch.py # This is a child entity, handle the naming in button.py and switch.py
return return
area = _area_name_from_id(self._smartbridge.areas, device["area"])
area, name = _area_and_name_from_name(device["name"]) name = device["name"].split("_")[-1]
self._attr_name = full_name = f"{area} {name}" self._attr_name = full_name = f"{area} {name}"
info = DeviceInfo( info = DeviceInfo(
# Historically we used the device serial number for the identifier # Historically we used the device serial number for the identifier

View File

@ -6,13 +6,14 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_SUGGESTED_AREA
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_and_name_from_name from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_name_from_id
from .const import CONFIG_URL, MANUFACTURER from .const import CONFIG_URL, MANUFACTURER, UNASSIGNED_AREA
from .models import LutronCasetaData from .models import LutronCasetaData
@ -43,7 +44,8 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
def __init__(self, device, data): def __init__(self, device, data):
"""Init an occupancy sensor.""" """Init an occupancy sensor."""
super().__init__(device, data) super().__init__(device, data)
_, name = _area_and_name_from_name(device["name"]) area = _area_name_from_id(self._smartbridge.areas, device["area"])
name = f"{area} {device['device_name']}"
self._attr_name = name self._attr_name = name
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(CASETA_DOMAIN, self.unique_id)}, identifiers={(CASETA_DOMAIN, self.unique_id)},
@ -54,6 +56,8 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
configuration_url=CONFIG_URL, configuration_url=CONFIG_URL,
entry_type=DeviceEntryType.SERVICE, entry_type=DeviceEntryType.SERVICE,
) )
if area != UNASSIGNED_AREA:
self._attr_device_info[ATTR_SUGGESTED_AREA] = area
@property @property
def is_on(self): def is_on(self):

View File

@ -9,7 +9,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import _area_and_name_from_name
from .const import DOMAIN as CASETA_DOMAIN from .const import DOMAIN as CASETA_DOMAIN
from .models import LutronCasetaData from .models import LutronCasetaData
from .util import serial_to_unique_id from .util import serial_to_unique_id
@ -42,7 +41,7 @@ class LutronCasetaScene(Scene):
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(CASETA_DOMAIN, data.bridge_device["serial"])}, identifiers={(CASETA_DOMAIN, data.bridge_device["serial"])},
) )
self._attr_name = _area_and_name_from_name(scene["name"])[1] self._attr_name = scene["name"]
self._attr_unique_id = f"scene_{bridge_unique_id}_{self._scene_id}" self._attr_unique_id = f"scene_{bridge_unique_id}_{self._scene_id}"
async def async_activate(self, **kwargs: Any) -> None: async def async_activate(self, **kwargs: Any) -> None:

View File

@ -105,7 +105,7 @@ class MockBridge:
"""Initialize MockBridge instance with configured mock connectivity.""" """Initialize MockBridge instance with configured mock connectivity."""
self.can_connect = can_connect self.can_connect = can_connect
self.is_currently_connected = False self.is_currently_connected = False
self.areas = {} self.areas = self.load_areas()
self.occupancy_groups = {} self.occupancy_groups = {}
self.scenes = self.get_scenes() self.scenes = self.get_scenes()
self.devices = self.load_devices() self.devices = self.load_devices()
@ -126,10 +126,28 @@ class MockBridge:
"""Return whether the mock bridge is connected.""" """Return whether the mock bridge is connected."""
return self.is_currently_connected return self.is_currently_connected
def load_areas(self):
"""Loak mock areas into self.areas."""
return {
"898": {"id": "898", "name": "Basement", "parent_id": None},
"822": {"id": "822", "name": "Bedroom", "parent_id": "898"},
"910": {"id": "910", "name": "Bathroom", "parent_id": "898"},
"1024": {"id": "1024", "name": "Master Bedroom", "parent_id": None},
"1025": {"id": "1025", "name": "Kitchen", "parent_id": None},
"1026": {"id": "1026", "name": "Dining Room", "parent_id": None},
"1205": {"id": "1205", "name": "Hallway", "parent_id": None},
}
def load_devices(self): def load_devices(self):
"""Load mock devices into self.devices.""" """Load mock devices into self.devices."""
return { return {
"1": {"serial": 1234, "name": "bridge", "model": "model", "type": "type"}, "1": {
"serial": 1234,
"name": "bridge",
"model": "model",
"type": "type",
"area": "1205",
},
"801": { "801": {
"device_id": "801", "device_id": "801",
"current_state": 100, "current_state": 100,
@ -141,6 +159,7 @@ class MockBridge:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "822",
}, },
"802": { "802": {
"device_id": "802", "device_id": "802",
@ -153,6 +172,7 @@ class MockBridge:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "822",
}, },
"803": { "803": {
"device_id": "803", "device_id": "803",
@ -165,6 +185,7 @@ class MockBridge:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "910",
}, },
"804": { "804": {
"device_id": "804", "device_id": "804",
@ -177,6 +198,7 @@ class MockBridge:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "1024",
}, },
"901": { "901": {
"device_id": "901", "device_id": "901",
@ -189,6 +211,7 @@ class MockBridge:
"model": None, "model": None,
"serial": 5442321, "serial": 5442321,
"tilt": None, "tilt": None,
"area": "1025",
}, },
"9": { "9": {
"device_id": "9", "device_id": "9",
@ -203,7 +226,7 @@ class MockBridge:
"model": "PJ2-3BRL-GXX-X01", "model": "PJ2-3BRL-GXX-X01",
"serial": 68551522, "serial": 68551522,
"device_name": "Pico", "device_name": "Pico",
"area": "6", "area": "1026",
}, },
"1355": { "1355": {
"device_id": "1355", "device_id": "1355",

View File

@ -40,7 +40,15 @@ async def test_diagnostics(hass, hass_client) -> None:
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
assert diag == { assert diag == {
"data": { "data": {
"areas": {}, "areas": {
"898": {"id": "898", "name": "Basement", "parent_id": None},
"822": {"id": "822", "name": "Bedroom", "parent_id": "898"},
"910": {"id": "910", "name": "Bathroom", "parent_id": "898"},
"1024": {"id": "1024", "name": "Master Bedroom", "parent_id": None},
"1025": {"id": "1025", "name": "Kitchen", "parent_id": None},
"1026": {"id": "1026", "name": "Dining Room", "parent_id": None},
"1205": {"id": "1205", "name": "Hallway", "parent_id": None},
},
"buttons": { "buttons": {
"111": { "111": {
"device_id": "111", "device_id": "111",
@ -73,6 +81,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"name": "bridge", "name": "bridge",
"serial": 1234, "serial": 1234,
"type": "type", "type": "type",
"area": "1205",
}, },
"801": { "801": {
"device_id": "801", "device_id": "801",
@ -85,6 +94,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "822",
}, },
"802": { "802": {
"device_id": "802", "device_id": "802",
@ -97,6 +107,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "822",
}, },
"803": { "803": {
"device_id": "803", "device_id": "803",
@ -109,6 +120,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "910",
}, },
"804": { "804": {
"device_id": "804", "device_id": "804",
@ -121,6 +133,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None, "model": None,
"serial": None, "serial": None,
"tilt": None, "tilt": None,
"area": "1024",
}, },
"901": { "901": {
"device_id": "901", "device_id": "901",
@ -133,6 +146,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None, "model": None,
"serial": 5442321, "serial": 5442321,
"tilt": None, "tilt": None,
"area": "1025",
}, },
"9": { "9": {
"device_id": "9", "device_id": "9",
@ -147,7 +161,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": "PJ2-3BRL-GXX-X01", "model": "PJ2-3BRL-GXX-X01",
"serial": 68551522, "serial": 68551522,
"device_name": "Pico", "device_name": "Pico",
"area": "6", "area": "1026",
}, },
"1355": { "1355": {
"device_id": "1355", "device_id": "1355",