diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 64620927..6d36ae2e 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -273,6 +273,8 @@ VALIDATION_TUPLES = [ ), ] +CONST_COLOR = "color" + def timedelta_as_int(value): """Convert a `datetime.timedelta` object to an integer. diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 98747a17..3eabf7ff 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -23,7 +23,11 @@ from homeassistant.components.light import ( ATTR_COLOR_NAME, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, + ATTR_MAX_COLOR_TEMP_KELVIN, + ATTR_MIN_COLOR_TEMP_KELVIN, ATTR_RGB_COLOR, + ATTR_RGBW_COLOR, + ATTR_RGBWW_COLOR, ATTR_SUPPORTED_COLOR_MODES, ATTR_TRANSITION, ATTR_XY_COLOR, @@ -32,6 +36,7 @@ from homeassistant.components.light import ( COLOR_MODE_HS, COLOR_MODE_RGB, COLOR_MODE_RGBW, + COLOR_MODE_RGBWW, COLOR_MODE_XY, ) from homeassistant.components.light import ( @@ -129,6 +134,7 @@ from .const import ( CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, + CONST_COLOR, DOMAIN, EXTRA_VALIDATION, ICON_BRIGHTNESS, @@ -155,6 +161,16 @@ _SUPPORT_OPTS = { "transition": SUPPORT_TRANSITION, } +VALID_COLOR_MODES = { + COLOR_MODE_BRIGHTNESS: ATTR_BRIGHTNESS, + COLOR_MODE_COLOR_TEMP: ATTR_COLOR_TEMP_KELVIN, + COLOR_MODE_HS: ATTR_HS_COLOR, + COLOR_MODE_RGB: ATTR_RGB_COLOR, + COLOR_MODE_RGBW: ATTR_RGBW_COLOR, + COLOR_MODE_RGBWW: ATTR_RGBWW_COLOR, + COLOR_MODE_XY: ATTR_XY_COLOR, +} + _ORDER = (SUN_EVENT_SUNRISE, SUN_EVENT_NOON, SUN_EVENT_SUNSET, SUN_EVENT_MIDNIGHT) _ALLOWED_ORDERS = {_ORDER[i:] + _ORDER[:i] for i in range(len(_ORDER))} @@ -623,33 +639,51 @@ def _expand_light_groups(hass: HomeAssistant, lights: list[str]) -> list[str]: return list(all_lights) +def _supported_to_attributes(supported): + supported_attributes = {} + supports_colors = False + for mode, attr in VALID_COLOR_MODES.items(): + if mode not in supported: + continue + supported_attributes[attr] = True + if ( + not supports_colors + and mode != COLOR_MODE_BRIGHTNESS + and mode != COLOR_MODE_COLOR_TEMP + ): + supports_colors = True + return supported_attributes, supports_colors + + def _supported_features(hass: HomeAssistant, light: str): state = hass.states.get(light) - supported_features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - supported = { - key for key, value in _SUPPORT_OPTS.items() if supported_features & value + legacy_supported_features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + legacy_supported = { + key for key, value in _SUPPORT_OPTS.items() if legacy_supported_features & value } supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES, set()) - if COLOR_MODE_RGB in supported_color_modes: - supported.add("color") + supported, supports_colors = _supported_to_attributes( + legacy_supported.union(supported_color_modes) + ) + min_kelvin = state.attributes.get(ATTR_MIN_COLOR_TEMP_KELVIN) + max_kelvin = state.attributes.get(ATTR_MAX_COLOR_TEMP_KELVIN) + supported.update( + { + ATTR_MIN_COLOR_TEMP_KELVIN: min_kelvin, + ATTR_MAX_COLOR_TEMP_KELVIN: max_kelvin, + } + ) + if supports_colors: # Adding brightness here, see # comment https://github.com/basnijholt/adaptive-lighting/issues/112#issuecomment-836944011 - supported.add("brightness") - if COLOR_MODE_RGBW in supported_color_modes: - supported.add("color") - supported.add("brightness") # see above url - if COLOR_MODE_XY in supported_color_modes: - supported.add("color") - supported.add("brightness") # see above url - if COLOR_MODE_HS in supported_color_modes: - supported.add("color") - supported.add("brightness") # see above url - if COLOR_MODE_COLOR_TEMP in supported_color_modes: - supported.add("color_temp") - supported.add("brightness") # see above url - if COLOR_MODE_BRIGHTNESS in supported_color_modes: - supported.add("brightness") - return supported + supported[ATTR_BRIGHTNESS] = True + if CONST_COLOR not in legacy_supported: + # supports_colors = False + _LOGGER.debug( + "'supported_color_modes' supports color but the legacy 'supported_features'" + " bitfield says we do not. Despite this we'll assume light '%s' supports colors", + ) + return supported, supports_colors def color_difference_redmean( @@ -1104,12 +1138,12 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): # Build service data. service_data = {ATTR_ENTITY_ID: light} - features = _supported_features(self.hass, light) + features, supports_colors = _supported_features(self.hass, light) # Check transition == 0 to fix #378 - if "transition" in features and transition > 0: + if ATTR_TRANSITION in features and transition > 0: service_data[ATTR_TRANSITION] = transition - if "brightness" in features and adapt_brightness: + if ATTR_BRIGHTNESS in features and adapt_brightness: brightness = round(255 * self._settings["brightness_pct"] / 100) service_data[ATTR_BRIGHTNESS] = brightness @@ -1118,19 +1152,18 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): and self._sun_light_settings.sleep_rgb_or_color_temp == "rgb_color" ) if ( - "color_temp" in features + ATTR_COLOR_TEMP_KELVIN in features and adapt_color - and not (prefer_rgb_color and "color" in features) - and not (sleep_rgb and "color" in features) + and not (prefer_rgb_color and supports_colors) + and not (sleep_rgb and supports_colors) ): _LOGGER.debug("%s: Setting color_temp of light %s", self._name, light) - attributes = self.hass.states.get(light).attributes - min_kelvin = attributes["min_color_temp_kelvin"] - max_kelvin = attributes["max_color_temp_kelvin"] + min_kelvin = features[ATTR_MIN_COLOR_TEMP_KELVIN] + max_kelvin = features[ATTR_MAX_COLOR_TEMP_KELVIN] color_temp_kelvin = self._settings["color_temp_kelvin"] color_temp_kelvin = max(min(color_temp_kelvin, max_kelvin), min_kelvin) service_data[ATTR_COLOR_TEMP_KELVIN] = color_temp_kelvin - elif "color" in features and adapt_color: + elif supports_colors and adapt_color: _LOGGER.debug("%s: Setting rgb_color of light %s", self._name, light) service_data[ATTR_RGB_COLOR] = self._settings["rgb_color"]