mirror of
https://github.com/home-assistant/core.git
synced 2025-12-21 11:30:03 +00:00
208 lines
6.9 KiB
Python
208 lines
6.9 KiB
Python
"""Light platform for UniFi Network integration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Coroutine
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, Any, cast
|
|
|
|
from aiounifi.interfaces.api_handlers import APIHandler, ItemEvent
|
|
from aiounifi.interfaces.devices import Devices
|
|
from aiounifi.models.api import ApiItem
|
|
from aiounifi.models.device import Device, DeviceSetLedStatus
|
|
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_RGB_COLOR,
|
|
ColorMode,
|
|
LightEntity,
|
|
LightEntityDescription,
|
|
LightEntityFeature,
|
|
)
|
|
from homeassistant.const import EntityCategory
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
from homeassistant.util.color import rgb_hex_to_rgb_list
|
|
|
|
from . import UnifiConfigEntry
|
|
from .entity import (
|
|
UnifiEntity,
|
|
UnifiEntityDescription,
|
|
async_device_available_fn,
|
|
async_device_device_info_fn,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from .hub import UnifiHub
|
|
|
|
|
|
def convert_brightness_to_unifi(ha_brightness: int) -> int:
|
|
"""Convert Home Assistant brightness (0-255) to UniFi brightness (0-100)."""
|
|
return round((ha_brightness / 255) * 100)
|
|
|
|
|
|
def convert_brightness_to_ha(
|
|
unifi_brightness: int,
|
|
) -> int:
|
|
"""Convert UniFi brightness (0-100) to Home Assistant brightness (0-255)."""
|
|
return round((unifi_brightness / 100) * 255)
|
|
|
|
|
|
def get_device_brightness_or_default(device: Device) -> int:
|
|
"""Get device's current LED brightness. Defaults to 100 (full brightness) if not set."""
|
|
value = device.led_override_color_brightness
|
|
return value if value is not None else 100
|
|
|
|
|
|
@callback
|
|
def async_device_led_supported_fn(hub: UnifiHub, obj_id: str) -> bool:
|
|
"""Check if device supports LED control."""
|
|
device: Device = hub.api.devices[obj_id]
|
|
return device.led_override is not None or device.supports_led_ring
|
|
|
|
|
|
@callback
|
|
def async_device_led_is_on_fn(hub: UnifiHub, device: Device) -> bool:
|
|
"""Check if device LED is on."""
|
|
return device.led_override == "on"
|
|
|
|
|
|
async def async_device_led_control_fn(
|
|
hub: UnifiHub, obj_id: str, turn_on: bool, **kwargs: Any
|
|
) -> None:
|
|
"""Control device LED."""
|
|
device = hub.api.devices[obj_id]
|
|
|
|
status = "on" if turn_on else "off"
|
|
|
|
# Only send brightness and RGB if device has LED_RING hardware support
|
|
if device.supports_led_ring:
|
|
# Use provided brightness or fall back to device's current brightness
|
|
if ATTR_BRIGHTNESS in kwargs:
|
|
brightness = convert_brightness_to_unifi(kwargs[ATTR_BRIGHTNESS])
|
|
else:
|
|
brightness = get_device_brightness_or_default(device)
|
|
|
|
# Use provided RGB color or fall back to device's current color
|
|
color: str | None
|
|
if ATTR_RGB_COLOR in kwargs:
|
|
rgb = kwargs[ATTR_RGB_COLOR]
|
|
color = f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}"
|
|
else:
|
|
color = device.led_override_color
|
|
else:
|
|
brightness = None
|
|
color = None
|
|
|
|
await hub.api.request(
|
|
DeviceSetLedStatus.create(
|
|
device=device,
|
|
status=status,
|
|
brightness=brightness,
|
|
color=color,
|
|
)
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True, kw_only=True)
|
|
class UnifiLightEntityDescription[HandlerT: APIHandler, ApiItemT: ApiItem](
|
|
LightEntityDescription, UnifiEntityDescription[HandlerT, ApiItemT]
|
|
):
|
|
"""Class describing UniFi light entity."""
|
|
|
|
control_fn: Callable[[UnifiHub, str, bool], Coroutine[Any, Any, None]]
|
|
is_on_fn: Callable[[UnifiHub, ApiItemT], bool]
|
|
|
|
|
|
ENTITY_DESCRIPTIONS: tuple[UnifiLightEntityDescription, ...] = (
|
|
UnifiLightEntityDescription[Devices, Device](
|
|
key="LED control",
|
|
translation_key="led_control",
|
|
entity_category=EntityCategory.CONFIG,
|
|
allowed_fn=lambda hub, obj_id: True,
|
|
api_handler_fn=lambda api: api.devices,
|
|
available_fn=async_device_available_fn,
|
|
control_fn=async_device_led_control_fn,
|
|
device_info_fn=async_device_device_info_fn,
|
|
is_on_fn=async_device_led_is_on_fn,
|
|
name_fn=lambda device: "LED",
|
|
object_fn=lambda api, obj_id: api.devices[obj_id],
|
|
supported_fn=async_device_led_supported_fn,
|
|
unique_id_fn=lambda hub, obj_id: f"led-{obj_id}",
|
|
),
|
|
)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: UnifiConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up lights for UniFi Network integration."""
|
|
config_entry.runtime_data.entity_loader.register_platform(
|
|
async_add_entities,
|
|
UnifiLightEntity,
|
|
ENTITY_DESCRIPTIONS,
|
|
requires_admin=True,
|
|
)
|
|
|
|
|
|
class UnifiLightEntity[HandlerT: APIHandler, ApiItemT: ApiItem](
|
|
UnifiEntity[HandlerT, ApiItemT], LightEntity
|
|
):
|
|
"""Base representation of a UniFi light."""
|
|
|
|
entity_description: UnifiLightEntityDescription[HandlerT, ApiItemT]
|
|
_attr_supported_features = LightEntityFeature(0)
|
|
|
|
@callback
|
|
def async_initiate_state(self) -> None:
|
|
"""Initiate entity state."""
|
|
device = cast(Device, self.entity_description.object_fn(self.api, self._obj_id))
|
|
|
|
if device.supports_led_ring:
|
|
self._attr_supported_color_modes = {ColorMode.RGB}
|
|
self._attr_color_mode = ColorMode.RGB
|
|
else:
|
|
self._attr_supported_color_modes = {ColorMode.ONOFF}
|
|
self._attr_color_mode = ColorMode.ONOFF
|
|
|
|
self.async_update_state(ItemEvent.ADDED, self._obj_id)
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn on light."""
|
|
await self.entity_description.control_fn(self.hub, self._obj_id, True, **kwargs)
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn off light."""
|
|
await self.entity_description.control_fn(
|
|
self.hub, self._obj_id, False, **kwargs
|
|
)
|
|
|
|
@callback
|
|
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
|
"""Update entity state."""
|
|
description = self.entity_description
|
|
device_obj = description.object_fn(self.api, self._obj_id)
|
|
device = cast(Device, device_obj)
|
|
|
|
self._attr_is_on = description.is_on_fn(self.hub, device_obj)
|
|
|
|
# Only set brightness and RGB if device has LED_RING hardware support
|
|
if device.supports_led_ring:
|
|
self._attr_brightness = convert_brightness_to_ha(
|
|
get_device_brightness_or_default(device)
|
|
)
|
|
|
|
# Parse hex color from device and convert to RGB tuple
|
|
hex_color = (
|
|
device.led_override_color.lstrip("#")
|
|
if self._attr_is_on and device.led_override_color
|
|
else None
|
|
)
|
|
if hex_color and len(hex_color) == 6:
|
|
rgb_list = rgb_hex_to_rgb_list(hex_color)
|
|
self._attr_rgb_color = (rgb_list[0], rgb_list[1], rgb_list[2])
|
|
else:
|
|
self._attr_rgb_color = None
|