diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 75f7bbd..6e7556b 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -53,6 +53,7 @@ CONF_BRIGHTNESS_LOWER = "brightness_lower" CONF_BRIGHTNESS_UPPER = "brightness_upper" CONF_COLOR = "color" CONF_COLOR_MODE = "color_mode" +CONF_COLOR_MODE_SET = "color_mode_set" CONF_COLOR_TEMP_MIN_KELVIN = "color_temp_min_kelvin" CONF_COLOR_TEMP_MAX_KELVIN = "color_temp_max_kelvin" CONF_COLOR_TEMP_REVERSE = "color_temp_reverse" diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index 7c74e49..bc70594 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -1,21 +1,20 @@ """Platform to locally control Tuya-based light devices.""" import logging import textwrap +from dataclasses import dataclass from functools import partial import homeassistant.util.color as color_util import voluptuous as vol +from homeassistant.components.ecobee.humidifier import MODE_MANUAL from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, DOMAIN, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, LightEntity, + LightEntityFeature, ColorMode, ) from homeassistant.const import CONF_BRIGHTNESS, CONF_COLOR_TEMP, CONF_SCENE @@ -28,7 +27,7 @@ from .const import ( CONF_COLOR_TEMP_MAX_KELVIN, CONF_COLOR_TEMP_MIN_KELVIN, CONF_COLOR_TEMP_REVERSE, - CONF_MUSIC_MODE, + CONF_MUSIC_MODE, CONF_COLOR_MODE_SET, ) _LOGGER = logging.getLogger(__name__) @@ -49,6 +48,8 @@ MODE_WHITE = "white" SCENE_CUSTOM = "Custom" SCENE_MUSIC = "Music" +MODES_SET = {"Colour, Music, Scene and White": 0, "Manual, Music, Scene and White": 1} + SCENE_LIST_RGBW_1000 = { "Night": "000e0d0000000000000000c80000", "Read": "010e0d0000000000000003e801f4", @@ -91,6 +92,22 @@ SCENE_LIST_RGB_1000 = { + "0000000", } +@dataclass(frozen=True) +class Mode: + color: str = MODE_COLOR + music: str = MODE_MUSIC + scene: str = MODE_SCENE + white: str = MODE_WHITE + + def as_list(self) -> list: + return [self.color, self.music, self.scene, self.white] + + def as_dict(self) -> dict[str, str]: + default = {"Default": self.white} + return {**default, "Mode Color": self.color, "Mode Scene": self.scene} + +MAP_MODE_SET = {0: Mode(), 1: Mode(color=MODE_MANUAL)} + def map_range(value, from_lower, from_upper, to_lower, to_upper): """Map a value in one range to another.""" @@ -162,6 +179,7 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): self._color_temp_reverse = self._config.get( CONF_COLOR_TEMP_REVERSE, DEFAULT_COLOR_TEMP_REVERSE ) + self._modes = MAP_MODE_SET[int(self._config.get(CONF_COLOR_MODE_SET, 0))] self._hs = None self._effect = None self._effect_list = [] @@ -197,8 +215,8 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): if self.is_color_mode: return self._hs if ( - self.supported_features & SUPPORT_COLOR - and not self.supported_features & SUPPORT_COLOR_TEMP + ColorMode.HS in self.supported_color_modes + and not ColorMode.COLOR_TEMP in self.supported_color_modes ): return [0, 0] return None @@ -244,24 +262,36 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): return self._effect_list @property - def supported_features(self): - """Flag supported features.""" - supports = 0 - if self.has_config(CONF_BRIGHTNESS): - supports |= SUPPORT_BRIGHTNESS + def supported_color_modes(self) -> set[ColorMode] | set[str] | None: + """Flag supported color modes.""" + color_modes: set[ColorMode] = set() + if self.has_config(CONF_COLOR_TEMP): - supports |= SUPPORT_COLOR_TEMP + color_modes.add(ColorMode.COLOR_TEMP) if self.has_config(CONF_COLOR): - supports |= SUPPORT_COLOR | SUPPORT_BRIGHTNESS + color_modes.add(ColorMode.HS) + + if not color_modes and self.has_config(CONF_BRIGHTNESS): + return {ColorMode.BRIGHTNESS} + + if not color_modes: + return {ColorMode.ONOFF} + + return color_modes + + @property + def supported_features(self) -> LightEntityFeature: + """Flag supported features.""" + supports = LightEntityFeature(0) if self.has_config(CONF_SCENE) or self.has_config(CONF_MUSIC_MODE): - supports |= SUPPORT_EFFECT + supports |= LightEntityFeature.EFFECT return supports @property def is_white_mode(self): """Return true if the light is in white mode.""" color_mode = self.__get_color_mode() - return color_mode is None or color_mode == MODE_WHITE + return color_mode is None or color_mode == self._modes.white @property def is_color_mode(self): @@ -304,7 +334,7 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): states[self._dp_id] = True features = self.supported_features brightness = None - if ATTR_EFFECT in kwargs and (features & SUPPORT_EFFECT): + if ATTR_EFFECT in kwargs and (features & LightEntityFeature.EFFECT): scene = self._scenes.get(kwargs[ATTR_EFFECT]) if scene is not None: if scene.startswith(MODE_SCENE): @@ -315,7 +345,11 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): elif kwargs[ATTR_EFFECT] == SCENE_MUSIC: states[self._config.get(CONF_COLOR_MODE)] = MODE_MUSIC - if ATTR_BRIGHTNESS in kwargs and (features & SUPPORT_BRIGHTNESS): + if ATTR_BRIGHTNESS in kwargs and ( + ColorMode.BRIGHTNESS in self.supported_color_modes + or self.has_config(CONF_BRIGHTNESS) + or self.has_config(CONF_COLOR) + ): brightness = map_range( int(kwargs[ATTR_BRIGHTNESS]), 0, @@ -347,7 +381,7 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): states[self._config.get(CONF_COLOR)] = color states[self._config.get(CONF_COLOR_MODE)] = MODE_COLOR - if ATTR_HS_COLOR in kwargs and (features & SUPPORT_COLOR): + if ATTR_HS_COLOR in kwargs and ColorMode.HS in self.supported_color_modes: if brightness is None: brightness = self._brightness hs = kwargs[ATTR_HS_COLOR] @@ -374,7 +408,7 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): states[self._config.get(CONF_COLOR)] = color states[self._config.get(CONF_COLOR_MODE)] = MODE_COLOR - if ATTR_COLOR_TEMP in kwargs and (features & SUPPORT_COLOR_TEMP): + if ATTR_COLOR_TEMP in kwargs and (features & ColorMode.RGB): if brightness is None: brightness = self._brightness mired = int(kwargs[ATTR_COLOR_TEMP]) @@ -403,10 +437,17 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): self._state = self.dps(self._dp_id) supported = self.supported_features self._effect = None - if supported & SUPPORT_BRIGHTNESS and self.has_config(CONF_BRIGHTNESS): + + if (ColorMode.BRIGHTNESS in self.supported_color_modes + or self.has_config(CONF_BRIGHTNESS) + or self.has_config(CONF_COLOR) + ): self._brightness = self.dps_conf(CONF_BRIGHTNESS) - if supported & SUPPORT_COLOR: + if brightness_dp_value := self.dp_value(CONF_BRIGHTNESS, None): + self._brightness = brightness_dp_value + + if ColorMode.HS in self.supported_color_modes: color = self.dps_conf(CONF_COLOR) if color is not None and not self.is_white_mode: if self.__is_color_rgb_encoded(): @@ -422,10 +463,10 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): self._hs = [hue, sat / 10.0] self._brightness = value - if supported & SUPPORT_COLOR_TEMP: + if ColorMode.COLOR_TEMP in self.supported_color_modes: self._color_temp = self.dps_conf(CONF_COLOR_TEMP) - if self.is_scene_mode and supported & SUPPORT_EFFECT: + if self.is_scene_mode and supported & LightEntityFeature.EFFECT: if self.dps_conf(CONF_COLOR_MODE) != MODE_SCENE: self._effect = self.__find_scene_by_scene_data( self.dps_conf(CONF_COLOR_MODE) @@ -440,7 +481,7 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): elif SCENE_CUSTOM in self._effect_list: self._effect_list.remove(SCENE_CUSTOM) - if self.is_music_mode and supported & SUPPORT_EFFECT: + if self.is_music_mode and supported & LightEntityFeature.EFFECT: self._effect = SCENE_MUSIC