"""Support for LED lights.""" import logging from typing import Any, Callable, List, Optional, Tuple from wled import WLED, Effect, WLEDError from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_WHITE_VALUE, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, Light, ) from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import HomeAssistantType import homeassistant.util.color as color_util from . import WLEDDeviceEntity from .const import ( ATTR_COLOR_PRIMARY, ATTR_INTENSITY, ATTR_ON, ATTR_PALETTE, ATTR_PLAYLIST, ATTR_PRESET, ATTR_SEGMENT_ID, ATTR_SPEED, DATA_WLED_CLIENT, DOMAIN, ) _LOGGER = logging.getLogger(__name__) PARALLEL_UPDATES = 1 async def async_setup_entry( hass: HomeAssistantType, entry: ConfigEntry, async_add_entities: Callable[[List[Entity], bool], None], ) -> None: """Set up WLED light based on a config entry.""" wled: WLED = hass.data[DOMAIN][entry.entry_id][DATA_WLED_CLIENT] # Does the WLED device support RGBW rgbw = wled.device.info.leds.rgbw # List of supported effects effects = wled.device.effects # WLED supports splitting a strip in multiple segments # Each segment will be a separate light in Home Assistant lights = [] for light in wled.device.state.segments: lights.append(WLEDLight(entry.entry_id, wled, light.segment_id, rgbw, effects)) async_add_entities(lights, True) class WLEDLight(Light, WLEDDeviceEntity): """Defines a WLED light.""" def __init__( self, entry_id: str, wled: WLED, segment: int, rgbw: bool, effects: List[Effect] ): """Initialize WLED light.""" self._effects = effects self._rgbw = rgbw self._segment = segment self._brightness: Optional[int] = None self._color: Optional[Tuple[float, float]] = None self._effect: Optional[str] = None self._state: Optional[bool] = None self._white_value: Optional[int] = None # Only apply the segment ID if it is not the first segment name = wled.device.info.name if segment != 0: name += f" {segment}" super().__init__(entry_id, wled, name, "mdi:led-strip-variant") @property def unique_id(self) -> str: """Return the unique ID for this sensor.""" return f"{self.wled.device.info.mac_address}_{self._segment}" @property def hs_color(self) -> Optional[Tuple[float, float]]: """Return the hue and saturation color value [float, float].""" return self._color @property def effect(self) -> Optional[str]: """Return the current effect of the light.""" return self._effect @property def brightness(self) -> Optional[int]: """Return the brightness of this light between 1..255.""" return self._brightness @property def white_value(self) -> Optional[int]: """Return the white value of this light between 0..255.""" return self._white_value @property def supported_features(self) -> int: """Flag supported features.""" flags = ( SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_COLOR_TEMP | SUPPORT_EFFECT | SUPPORT_TRANSITION ) if self._rgbw: flags |= SUPPORT_WHITE_VALUE return flags @property def effect_list(self) -> List[str]: """Return the list of supported effects.""" return [effect.name for effect in self._effects] @property def is_on(self) -> bool: """Return the state of the light.""" return bool(self._state) async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the light.""" try: await self.wled.light(on=False) self._state = False except WLEDError: _LOGGER.error("An error occurred while turning off WLED light.") self._available = False self.async_schedule_update_ha_state() async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the light.""" data = {ATTR_ON: True, ATTR_SEGMENT_ID: self._segment} if ATTR_COLOR_TEMP in kwargs: mireds = color_util.color_temperature_kelvin_to_mired( kwargs[ATTR_COLOR_TEMP] ) data[ATTR_COLOR_PRIMARY] = tuple( map(int, color_util.color_temperature_to_rgb(mireds)) ) if ATTR_HS_COLOR in kwargs: hue, sat = kwargs[ATTR_HS_COLOR] data[ATTR_COLOR_PRIMARY] = color_util.color_hsv_to_RGB(hue, sat, 100) if ATTR_TRANSITION in kwargs: data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION] if ATTR_BRIGHTNESS in kwargs: data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] if ATTR_EFFECT in kwargs: data[ATTR_EFFECT] = kwargs[ATTR_EFFECT] # Support for RGBW strips, adds white value if self._rgbw and any( x in (ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_WHITE_VALUE) for x in kwargs ): # WLED cannot just accept a white value, it needs the color. # We use the last know color in case just the white value changes. if not any(x in (ATTR_COLOR_TEMP, ATTR_HS_COLOR) for x in kwargs): hue, sat = self._color data[ATTR_COLOR_PRIMARY] = color_util.color_hsv_to_RGB(hue, sat, 100) # Add requested or last known white value if ATTR_WHITE_VALUE in kwargs: data[ATTR_COLOR_PRIMARY] += (kwargs[ATTR_WHITE_VALUE],) else: data[ATTR_COLOR_PRIMARY] += (self._white_value,) try: await self.wled.light(**data) self._state = True if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] if ATTR_EFFECT in kwargs: self._effect = kwargs[ATTR_EFFECT] if ATTR_HS_COLOR in kwargs: self._color = kwargs[ATTR_HS_COLOR] if ATTR_COLOR_TEMP in kwargs: self._color = color_util.color_temperature_to_hs(mireds) if ATTR_WHITE_VALUE in kwargs: self._white_value = kwargs[ATTR_WHITE_VALUE] except WLEDError: _LOGGER.error("An error occurred while turning on WLED light.") self._available = False self.async_schedule_update_ha_state() async def _wled_update(self) -> None: """Update WLED entity.""" self._brightness = self.wled.device.state.brightness self._effect = self.wled.device.state.segments[self._segment].effect.name self._state = self.wled.device.state.on color = self.wled.device.state.segments[self._segment].color_primary self._color = color_util.color_RGB_to_hs(*color[:3]) if self._rgbw: self._white_value = color[-1] playlist = self.wled.device.state.playlist if playlist == -1: playlist = None preset = self.wled.device.state.preset if preset == -1: preset = None self._attributes = { ATTR_INTENSITY: self.wled.device.state.segments[self._segment].intensity, ATTR_PALETTE: self.wled.device.state.segments[self._segment].palette.name, ATTR_PLAYLIST: playlist, ATTR_PRESET: preset, ATTR_SPEED: self.wled.device.state.segments[self._segment].speed, }