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
_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(
hass, entry_id, bridge, bridge_device, buttons
)
@ -196,18 +196,25 @@ async def async_setup_entry(
@callback
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:
"""Register the bridge device in the device registry."""
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
name=bridge_device["name"],
manufacturer=MANUFACTURER,
config_entry_id=config_entry_id,
identifiers={(DOMAIN, bridge_device["serial"])},
model=f"{bridge_device['model']} ({bridge_device['type']})",
configuration_url="https://device-login.lutron.com",
)
device_args: DeviceInfo = {
"name": bridge_device["name"],
"manufacturer": MANUFACTURER,
"identifiers": {(DOMAIN, bridge_device["serial"])},
"model": f"{bridge_device['model']} ({bridge_device['type']})",
"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
@ -241,7 +248,10 @@ def _async_register_button_devices(
continue
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 = {
"name": f"{area} {name}",
"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']}"
def _area_and_name_from_name(device_name: str) -> tuple[str, str]:
"""Return the area and name from the devices internal name."""
if "_" in device_name:
area_device_name = device_name.split("_", 1)
return area_device_name[0], area_device_name[1]
return UNASSIGNED_AREA, device_name
def _area_name_from_id(areas: dict[str, dict], area_id: str) -> str:
"""Return the full area name including parent(s)."""
if area_id is None:
return UNASSIGNED_AREA
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
@ -316,7 +333,8 @@ def _async_subscribe_pico_remote_events(
)
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"]
lip_button_number = async_get_lip_button(type_, leap_button_number)
hass_device = dev_reg.async_get_device({(DOMAIN, ha_device_serial)})
@ -377,8 +395,8 @@ class LutronCasetaDevice(Entity):
if "parent_device" in device:
# This is a child entity, handle the naming in button.py and switch.py
return
area, name = _area_and_name_from_name(device["name"])
area = _area_name_from_id(self._smartbridge.areas, device["area"])
name = device["name"].split("_")[-1]
self._attr_name = full_name = f"{area} {name}"
info = DeviceInfo(
# Historically we used the device serial number for the identifier

View File

@ -6,13 +6,14 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_SUGGESTED_AREA
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_and_name_from_name
from .const import CONFIG_URL, MANUFACTURER
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_name_from_id
from .const import CONFIG_URL, MANUFACTURER, UNASSIGNED_AREA
from .models import LutronCasetaData
@ -43,7 +44,8 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
def __init__(self, device, data):
"""Init an occupancy sensor."""
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_device_info = DeviceInfo(
identifiers={(CASETA_DOMAIN, self.unique_id)},
@ -54,6 +56,8 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
configuration_url=CONFIG_URL,
entry_type=DeviceEntryType.SERVICE,
)
if area != UNASSIGNED_AREA:
self._attr_device_info[ATTR_SUGGESTED_AREA] = area
@property
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_platform import AddEntitiesCallback
from . import _area_and_name_from_name
from .const import DOMAIN as CASETA_DOMAIN
from .models import LutronCasetaData
from .util import serial_to_unique_id
@ -42,7 +41,7 @@ class LutronCasetaScene(Scene):
self._attr_device_info = DeviceInfo(
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}"
async def async_activate(self, **kwargs: Any) -> None:

View File

@ -105,7 +105,7 @@ class MockBridge:
"""Initialize MockBridge instance with configured mock connectivity."""
self.can_connect = can_connect
self.is_currently_connected = False
self.areas = {}
self.areas = self.load_areas()
self.occupancy_groups = {}
self.scenes = self.get_scenes()
self.devices = self.load_devices()
@ -126,10 +126,28 @@ class MockBridge:
"""Return whether the mock bridge is 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):
"""Load mock devices into self.devices."""
return {
"1": {"serial": 1234, "name": "bridge", "model": "model", "type": "type"},
"1": {
"serial": 1234,
"name": "bridge",
"model": "model",
"type": "type",
"area": "1205",
},
"801": {
"device_id": "801",
"current_state": 100,
@ -141,6 +159,7 @@ class MockBridge:
"model": None,
"serial": None,
"tilt": None,
"area": "822",
},
"802": {
"device_id": "802",
@ -153,6 +172,7 @@ class MockBridge:
"model": None,
"serial": None,
"tilt": None,
"area": "822",
},
"803": {
"device_id": "803",
@ -165,6 +185,7 @@ class MockBridge:
"model": None,
"serial": None,
"tilt": None,
"area": "910",
},
"804": {
"device_id": "804",
@ -177,6 +198,7 @@ class MockBridge:
"model": None,
"serial": None,
"tilt": None,
"area": "1024",
},
"901": {
"device_id": "901",
@ -189,6 +211,7 @@ class MockBridge:
"model": None,
"serial": 5442321,
"tilt": None,
"area": "1025",
},
"9": {
"device_id": "9",
@ -203,7 +226,7 @@ class MockBridge:
"model": "PJ2-3BRL-GXX-X01",
"serial": 68551522,
"device_name": "Pico",
"area": "6",
"area": "1026",
},
"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)
assert diag == {
"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": {
"111": {
"device_id": "111",
@ -73,6 +81,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"name": "bridge",
"serial": 1234,
"type": "type",
"area": "1205",
},
"801": {
"device_id": "801",
@ -85,6 +94,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None,
"serial": None,
"tilt": None,
"area": "822",
},
"802": {
"device_id": "802",
@ -97,6 +107,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None,
"serial": None,
"tilt": None,
"area": "822",
},
"803": {
"device_id": "803",
@ -109,6 +120,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None,
"serial": None,
"tilt": None,
"area": "910",
},
"804": {
"device_id": "804",
@ -121,6 +133,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None,
"serial": None,
"tilt": None,
"area": "1024",
},
"901": {
"device_id": "901",
@ -133,6 +146,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": None,
"serial": 5442321,
"tilt": None,
"area": "1025",
},
"9": {
"device_id": "9",
@ -147,7 +161,7 @@ async def test_diagnostics(hass, hass_client) -> None:
"model": "PJ2-3BRL-GXX-X01",
"serial": 68551522,
"device_name": "Pico",
"area": "6",
"area": "1026",
},
"1355": {
"device_id": "1355",