109 lines
3.8 KiB
Python
109 lines
3.8 KiB
Python
"""Component for interacting with a Lutron Caseta system."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from homeassistant.const import ATTR_SUGGESTED_AREA
|
|
from homeassistant.helpers.device_registry import DeviceInfo
|
|
from homeassistant.helpers.entity import Entity
|
|
|
|
from .const import CONFIG_URL, DOMAIN, MANUFACTURER, UNASSIGNED_AREA
|
|
from .models import LutronCasetaData
|
|
from .util import area_name_from_id, serial_to_unique_id
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class LutronCasetaEntity(Entity):
|
|
"""Common base class for all Lutron Caseta devices."""
|
|
|
|
_attr_should_poll = False
|
|
|
|
def __init__(self, device: dict[str, Any], data: LutronCasetaData) -> None:
|
|
"""Set up the base class.
|
|
|
|
[:param]device the device metadata
|
|
[:param]bridge the smartbridge object
|
|
[:param]bridge_device a dict with the details of the bridge
|
|
"""
|
|
self._device = device
|
|
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:
|
|
# This is a child entity, handle the naming in button.py and switch.py
|
|
return
|
|
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
|
|
# but the serial is usually an integer and a string is expected
|
|
# here. Since it would be a breaking change to change the identifier
|
|
# we are ignoring the type error here until it can be migrated to
|
|
# a string in a future release.
|
|
identifiers={
|
|
(
|
|
DOMAIN,
|
|
self._handle_none_serial(self.serial), # type: ignore[arg-type]
|
|
)
|
|
},
|
|
manufacturer=MANUFACTURER,
|
|
model=f"{device['model']} ({device['type']})",
|
|
name=full_name,
|
|
via_device=(DOMAIN, self._bridge_device["serial"]),
|
|
configuration_url=CONFIG_URL,
|
|
)
|
|
if area != UNASSIGNED_AREA:
|
|
info[ATTR_SUGGESTED_AREA] = area
|
|
self._attr_device_info = info
|
|
|
|
async def async_added_to_hass(self):
|
|
"""Register callbacks."""
|
|
self._smartbridge.add_subscriber(self.device_id, self.async_write_ha_state)
|
|
|
|
def _handle_none_serial(self, serial: str | int | None) -> str | int:
|
|
"""Handle None serial returned by RA3 and QSX processors."""
|
|
if serial is None:
|
|
return f"{self._bridge_unique_id}_{self.device_id}"
|
|
return serial
|
|
|
|
@property
|
|
def device_id(self):
|
|
"""Return the device ID used for calling pylutron_caseta."""
|
|
return self._device["device_id"]
|
|
|
|
@property
|
|
def serial(self) -> int | None:
|
|
"""Return the serial number of the device."""
|
|
return self._device["serial"]
|
|
|
|
@property
|
|
def unique_id(self) -> str:
|
|
"""Return the unique ID of the device (serial)."""
|
|
return str(self._handle_none_serial(self.serial))
|
|
|
|
@property
|
|
def extra_state_attributes(self):
|
|
"""Return the state attributes."""
|
|
attributes = {
|
|
"device_id": self.device_id,
|
|
}
|
|
if zone := self._device.get("zone"):
|
|
attributes["zone_id"] = zone
|
|
return attributes
|
|
|
|
|
|
class LutronCasetaUpdatableEntity(LutronCasetaEntity):
|
|
"""A lutron_caseta entity that can update by syncing data from the bridge."""
|
|
|
|
async def async_update(self) -> None:
|
|
"""Update when forcing a refresh of the device."""
|
|
self._device = self._smartbridge.get_device_by_id(self.device_id)
|
|
_LOGGER.debug(self._device)
|