Add device to Lutron (#107467)

* Add typing to Lutron platforms

* Add devices to Lutron

* Add devices to Lutron

* Fix typing

* Fix

* Add name

* Fix lights

* Comment out ESA

* Fix domain

* Fix domain

* Fix

* Make generic keypad base class
This commit is contained in:
Joost Lekkerkerker 2024-01-22 15:52:59 +01:00 committed by GitHub
parent 43daf20be3
commit d0da457a04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 50 deletions

View File

@ -17,6 +17,7 @@ from homeassistant.const import (
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
import homeassistant.helpers.config_validation as cv
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import slugify
@ -167,7 +168,7 @@ class LutronData:
buttons: list[LutronButton]
covers: list[tuple[str, Output]]
lights: list[tuple[str, Output]]
scenes: list[tuple[str, str, Button, Led]]
scenes: list[tuple[str, Keypad, Button, Led]]
switches: list[tuple[str, Output]]
@ -218,11 +219,20 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
(led for led in keypad.leds if led.number == button.number),
None,
)
entry_data.scenes.append((area.name, keypad.name, button, led))
entry_data.scenes.append((area.name, keypad, button, led))
entry_data.buttons.append(LutronButton(hass, area.name, keypad, button))
if area.occupancy_group is not None:
entry_data.binary_sensors.append((area.name, area.occupancy_group))
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={(DOMAIN, lutron_client.guid)},
manufacturer="Lutron",
name="Main repeater",
)
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = entry_data
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

View File

@ -58,14 +58,6 @@ class LutronOccupancySensor(LutronDevice, BinarySensorEntity):
# Error cases will end up treated as unoccupied.
return self._lutron_device.state == OccupancyGroup.State.OCCUPIED
@property
def name(self) -> str:
"""Return the name of the device."""
# The default LutronDevice naming would create 'Kitchen Occ Kitchen',
# but since there can only be one OccupancyGroup per area we go
# with something shorter.
return f"{self._area_name} Occupancy"
@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return the state attributes."""

View File

@ -51,6 +51,7 @@ class LutronCover(LutronDevice, CoverEntity):
| CoverEntityFeature.SET_POSITION
)
_lutron_device: Output
_attr_name = None
@property
def is_closed(self) -> bool:

View File

@ -1,14 +1,19 @@
"""Base class for Lutron devices."""
from pylutron import Lutron, LutronEntity, LutronEvent
from pylutron import Keypad, Lutron, LutronEntity, LutronEvent
from homeassistant.const import ATTR_IDENTIFIERS, ATTR_VIA_DEVICE
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from .const import DOMAIN
class LutronDevice(Entity):
"""Representation of a Lutron device entity."""
class LutronBaseEntity(Entity):
"""Base class for Lutron entities."""
_attr_should_poll = False
_attr_has_entity_name = True
def __init__(
self, area_name: str, lutron_device: LutronEntity, controller: Lutron
@ -28,11 +33,6 @@ class LutronDevice(Entity):
"""Run when invoked by pylutron when the device state changes."""
self.schedule_update_ha_state()
@property
def name(self) -> str:
"""Return the name of the device."""
return f"{self._area_name} {self._lutron_device.name}"
@property
def unique_id(self) -> str | None:
"""Return a unique ID."""
@ -40,3 +40,43 @@ class LutronDevice(Entity):
if self._lutron_device.uuid is None:
return None
return f"{self._controller.guid}_{self._lutron_device.uuid}"
class LutronDevice(LutronBaseEntity):
"""Representation of a Lutron device entity."""
def __init__(
self, area_name: str, lutron_device: LutronEntity, controller: Lutron
) -> None:
"""Initialize the device."""
super().__init__(area_name, lutron_device, controller)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, lutron_device.uuid)},
manufacturer="Lutron",
name=lutron_device.name,
suggested_area=area_name,
via_device=(DOMAIN, controller.guid),
)
class LutronKeypad(LutronBaseEntity):
"""Representation of a Lutron Keypad."""
def __init__(
self,
area_name: str,
lutron_device: LutronEntity,
controller: Lutron,
keypad: Keypad,
) -> None:
"""Initialize the device."""
super().__init__(area_name, lutron_device, controller)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, keypad.id)},
manufacturer="Lutron",
name=keypad.name,
)
if keypad.type == "MAIN_REPEATER":
self._attr_device_info[ATTR_IDENTIFIERS].add((DOMAIN, controller.guid))
else:
self._attr_device_info[ATTR_VIA_DEVICE] = (DOMAIN, controller.guid)

View File

@ -52,6 +52,7 @@ class LutronLight(LutronDevice, LightEntity):
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
_lutron_device: Output
_prev_brightness: int | None = None
_attr_name = None
@property
def brightness(self) -> int:

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any
from pylutron import Button, Led, Lutron
from pylutron import Button, Keypad, Lutron
from homeassistant.components.scene import Scene
from homeassistant.config_entries import ConfigEntry
@ -11,7 +11,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN, LutronData
from .entity import LutronDevice
from .entity import LutronKeypad
async def async_setup_entry(
@ -28,14 +28,14 @@ async def async_setup_entry(
async_add_entities(
[
LutronScene(area_name, keypad_name, device, led, entry_data.client)
for area_name, keypad_name, device, led in entry_data.scenes
LutronScene(area_name, keypad, device, entry_data.client)
for area_name, keypad, device, led in entry_data.scenes
],
True,
)
class LutronScene(LutronDevice, Scene):
class LutronScene(LutronKeypad, Scene):
"""Representation of a Lutron Scene."""
_lutron_device: Button
@ -43,21 +43,14 @@ class LutronScene(LutronDevice, Scene):
def __init__(
self,
area_name: str,
keypad_name: str,
keypad: Keypad,
lutron_device: Button,
lutron_led: Led,
controller: Lutron,
) -> None:
"""Initialize the scene/button."""
super().__init__(area_name, lutron_device, controller)
self._keypad_name = keypad_name
self._led = lutron_led
super().__init__(area_name, lutron_device, controller, keypad)
self._attr_name = lutron_device.name
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self._lutron_device.press()
@property
def name(self) -> str:
"""Return the name of the device."""
return f"{self._area_name} {self._keypad_name}: {self._lutron_device.name}"

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from pylutron import Button, Led, Lutron, Output
from pylutron import Button, Keypad, Led, Lutron, Output
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN, LutronData
from .entity import LutronDevice
from .entity import LutronDevice, LutronKeypad
async def async_setup_entry(
@ -33,11 +33,9 @@ async def async_setup_entry(
entities.append(LutronSwitch(area_name, device, entry_data.client))
# Add the indicator LEDs for scenes (keypad buttons)
for area_name, keypad_name, scene, led in entry_data.scenes:
for area_name, keypad, scene, led in entry_data.scenes:
if led is not None:
entities.append(
LutronLed(area_name, keypad_name, scene, led, entry_data.client)
)
entities.append(LutronLed(area_name, keypad, scene, led, entry_data.client))
async_add_entities(entities, True)
@ -77,7 +75,7 @@ class LutronSwitch(LutronDevice, SwitchEntity):
self._prev_state = self._lutron_device.level > 0
class LutronLed(LutronDevice, SwitchEntity):
class LutronLed(LutronKeypad, SwitchEntity):
"""Representation of a Lutron Keypad LED."""
_lutron_device: Led
@ -85,15 +83,15 @@ class LutronLed(LutronDevice, SwitchEntity):
def __init__(
self,
area_name: str,
keypad_name: str,
keypad: Keypad,
scene_device: Button,
led_device: Led,
controller: Lutron,
) -> None:
"""Initialize the switch."""
self._keypad_name = keypad_name
self._scene_name = scene_device.name
super().__init__(area_name, led_device, controller)
super().__init__(area_name, led_device, controller, keypad)
self._keypad_name = keypad.name
self._attr_name = scene_device.name
def turn_on(self, **kwargs: Any) -> None:
"""Turn the LED on."""
@ -108,7 +106,7 @@ class LutronLed(LutronDevice, SwitchEntity):
"""Return the state attributes."""
return {
"keypad": self._keypad_name,
"scene": self._scene_name,
"scene": self._attr_name,
"led": self._lutron_device.name,
}
@ -117,11 +115,6 @@ class LutronLed(LutronDevice, SwitchEntity):
"""Return true if device is on."""
return self._lutron_device.last_state
@property
def name(self) -> str:
"""Return the name of the LED."""
return f"{self._area_name} {self._keypad_name}: {self._scene_name} LED"
def update(self) -> None:
"""Call when forcing a refresh of the device."""
# The following property getter actually triggers an update in Lutron