core/homeassistant/components/lutron_caseta/entity.py

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)