|
| 1 | +"""Light platform for UniFi Network integration.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +from collections.abc import Callable, Coroutine |
| 6 | +from dataclasses import dataclass |
| 7 | +from typing import TYPE_CHECKING, Any, cast |
| 8 | + |
| 9 | +from aiounifi.interfaces.api_handlers import APIHandler, ItemEvent |
| 10 | +from aiounifi.interfaces.devices import Devices |
| 11 | +from aiounifi.models.api import ApiItem |
| 12 | +from aiounifi.models.device import Device, DeviceSetLedStatus |
| 13 | + |
| 14 | +from homeassistant.components.light import ( |
| 15 | + ATTR_BRIGHTNESS, |
| 16 | + ATTR_RGB_COLOR, |
| 17 | + ColorMode, |
| 18 | + LightEntity, |
| 19 | + LightEntityDescription, |
| 20 | + LightEntityFeature, |
| 21 | +) |
| 22 | +from homeassistant.core import HomeAssistant, callback |
| 23 | +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback |
| 24 | +from homeassistant.util.color import rgb_hex_to_rgb_list |
| 25 | + |
| 26 | +from . import UnifiConfigEntry |
| 27 | +from .entity import ( |
| 28 | + UnifiEntity, |
| 29 | + UnifiEntityDescription, |
| 30 | + async_device_available_fn, |
| 31 | + async_device_device_info_fn, |
| 32 | +) |
| 33 | + |
| 34 | +if TYPE_CHECKING: |
| 35 | + from .hub import UnifiHub |
| 36 | + |
| 37 | + |
| 38 | +@callback |
| 39 | +def async_device_led_supported_fn(hub: UnifiHub, obj_id: str) -> bool: |
| 40 | + """Check if device supports LED control.""" |
| 41 | + device: Device = hub.api.devices[obj_id] |
| 42 | + return device.supports_led_ring |
| 43 | + |
| 44 | + |
| 45 | +@callback |
| 46 | +def async_device_led_is_on_fn(hub: UnifiHub, device: Device) -> bool: |
| 47 | + """Check if device LED is on.""" |
| 48 | + return device.led_override == "on" |
| 49 | + |
| 50 | + |
| 51 | +async def async_device_led_control_fn( |
| 52 | + hub: UnifiHub, obj_id: str, turn_on: bool, **kwargs: Any |
| 53 | +) -> None: |
| 54 | + """Control device LED.""" |
| 55 | + device = hub.api.devices[obj_id] |
| 56 | + |
| 57 | + status = "on" if turn_on else "off" |
| 58 | + |
| 59 | + brightness = ( |
| 60 | + int((kwargs[ATTR_BRIGHTNESS] / 255) * 100) |
| 61 | + if ATTR_BRIGHTNESS in kwargs |
| 62 | + else device.led_override_color_brightness |
| 63 | + ) |
| 64 | + |
| 65 | + color = ( |
| 66 | + f"#{kwargs[ATTR_RGB_COLOR][0]:02x}{kwargs[ATTR_RGB_COLOR][1]:02x}{kwargs[ATTR_RGB_COLOR][2]:02x}" |
| 67 | + if ATTR_RGB_COLOR in kwargs |
| 68 | + else device.led_override_color |
| 69 | + ) |
| 70 | + |
| 71 | + await hub.api.request( |
| 72 | + DeviceSetLedStatus.create( |
| 73 | + device=device, |
| 74 | + status=status, |
| 75 | + brightness=brightness, |
| 76 | + color=color, |
| 77 | + ) |
| 78 | + ) |
| 79 | + |
| 80 | + |
| 81 | +@dataclass(frozen=True, kw_only=True) |
| 82 | +class UnifiLightEntityDescription[HandlerT: APIHandler, ApiItemT: ApiItem]( |
| 83 | + LightEntityDescription, UnifiEntityDescription[HandlerT, ApiItemT] |
| 84 | +): |
| 85 | + """Class describing UniFi light entity.""" |
| 86 | + |
| 87 | + control_fn: Callable[[UnifiHub, str, bool], Coroutine[Any, Any, None]] |
| 88 | + is_on_fn: Callable[[UnifiHub, ApiItemT], bool] |
| 89 | + |
| 90 | + |
| 91 | +ENTITY_DESCRIPTIONS: tuple[UnifiLightEntityDescription, ...] = ( |
| 92 | + UnifiLightEntityDescription[Devices, Device]( |
| 93 | + key="LED control", |
| 94 | + translation_key="led_control", |
| 95 | + allowed_fn=lambda hub, obj_id: True, |
| 96 | + api_handler_fn=lambda api: api.devices, |
| 97 | + available_fn=async_device_available_fn, |
| 98 | + control_fn=async_device_led_control_fn, |
| 99 | + device_info_fn=async_device_device_info_fn, |
| 100 | + is_on_fn=async_device_led_is_on_fn, |
| 101 | + name_fn=lambda device: "LED", |
| 102 | + object_fn=lambda api, obj_id: api.devices[obj_id], |
| 103 | + supported_fn=async_device_led_supported_fn, |
| 104 | + unique_id_fn=lambda hub, obj_id: f"led-{obj_id}", |
| 105 | + ), |
| 106 | +) |
| 107 | + |
| 108 | + |
| 109 | +async def async_setup_entry( |
| 110 | + hass: HomeAssistant, |
| 111 | + config_entry: UnifiConfigEntry, |
| 112 | + async_add_entities: AddConfigEntryEntitiesCallback, |
| 113 | +) -> None: |
| 114 | + """Set up lights for UniFi Network integration.""" |
| 115 | + config_entry.runtime_data.entity_loader.register_platform( |
| 116 | + async_add_entities, |
| 117 | + UnifiLightEntity, |
| 118 | + ENTITY_DESCRIPTIONS, |
| 119 | + requires_admin=True, |
| 120 | + ) |
| 121 | + |
| 122 | + |
| 123 | +class UnifiLightEntity[HandlerT: APIHandler, ApiItemT: ApiItem]( |
| 124 | + UnifiEntity[HandlerT, ApiItemT], LightEntity |
| 125 | +): |
| 126 | + """Base representation of a UniFi light.""" |
| 127 | + |
| 128 | + entity_description: UnifiLightEntityDescription[HandlerT, ApiItemT] |
| 129 | + _attr_supported_features = LightEntityFeature(0) |
| 130 | + _attr_color_mode = ColorMode.RGB |
| 131 | + _attr_supported_color_modes = {ColorMode.RGB} |
| 132 | + |
| 133 | + @callback |
| 134 | + def async_initiate_state(self) -> None: |
| 135 | + """Initiate entity state.""" |
| 136 | + self.async_update_state(ItemEvent.ADDED, self._obj_id) |
| 137 | + |
| 138 | + async def async_turn_on(self, **kwargs: Any) -> None: |
| 139 | + """Turn on light.""" |
| 140 | + await self.entity_description.control_fn(self.hub, self._obj_id, True, **kwargs) |
| 141 | + |
| 142 | + async def async_turn_off(self, **kwargs: Any) -> None: |
| 143 | + """Turn off light.""" |
| 144 | + await self.entity_description.control_fn( |
| 145 | + self.hub, self._obj_id, False, **kwargs |
| 146 | + ) |
| 147 | + |
| 148 | + @callback |
| 149 | + def async_update_state(self, event: ItemEvent, obj_id: str) -> None: |
| 150 | + """Update entity state.""" |
| 151 | + description = self.entity_description |
| 152 | + device_obj = description.object_fn(self.api, self._obj_id) |
| 153 | + |
| 154 | + device = cast(Device, device_obj) |
| 155 | + |
| 156 | + self._attr_is_on = description.is_on_fn(self.hub, device_obj) |
| 157 | + |
| 158 | + brightness = device.led_override_color_brightness |
| 159 | + self._attr_brightness = ( |
| 160 | + int((int(brightness) / 100) * 255) if brightness is not None else None |
| 161 | + ) |
| 162 | + |
| 163 | + hex_color = ( |
| 164 | + device.led_override_color.lstrip("#") |
| 165 | + if self._attr_is_on and device.led_override_color |
| 166 | + else None |
| 167 | + ) |
| 168 | + if hex_color and len(hex_color) == 6: |
| 169 | + rgb_list = rgb_hex_to_rgb_list(hex_color) |
| 170 | + self._attr_rgb_color = (rgb_list[0], rgb_list[1], rgb_list[2]) |
| 171 | + else: |
| 172 | + self._attr_rgb_color = None |
0 commit comments