diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 3aaf9dda..241689fc 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -289,8 +289,6 @@ 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 4cbd6854..2bc17862 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -18,12 +18,7 @@ import astral from homeassistant.components.light import ( ATTR_BRIGHTNESS, 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, @@ -141,7 +136,6 @@ from .const import ( CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, - CONST_COLOR, DOMAIN, EXTRA_VALIDATION, ICON_BRIGHTNESS, @@ -163,21 +157,10 @@ from .const import ( from .hass_utils import setup_service_call_interceptor _SUPPORT_OPTS = { - COLOR_MODE_BRIGHTNESS: SUPPORT_BRIGHTNESS, - COLOR_MODE_COLOR_TEMP: SUPPORT_COLOR_TEMP, - CONST_COLOR: SUPPORT_COLOR, - ATTR_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, + "brightness": SUPPORT_BRIGHTNESS, + "color_temp": SUPPORT_COLOR_TEMP, + "color": SUPPORT_COLOR, + "transition": SUPPORT_TRANSITION, } _ORDER = (SUN_EVENT_SUNRISE, SUN_EVENT_NOON, SUN_EVENT_SUNSET, SUN_EVENT_MIDNIGHT) @@ -625,53 +608,36 @@ 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 in supported: - attr = VALID_COLOR_MODES.get(mode) - if attr: - supported_attributes[attr] = True - if attr in COLOR_ATTRS: - supports_colors = True - # ATTR_SUPPORTED_FEATURES only - elif mode in _SUPPORT_OPTS: - supported_attributes[mode] = True - if CONST_COLOR in supported_attributes: - supports_colors = True - supported_attributes.pop(CONST_COLOR) - return supported_attributes, supports_colors - - def _supported_features(hass: HomeAssistant, light: str): state = hass.states.get(light) - 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_features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + supported = { + key for key, value in _SUPPORT_OPTS.items() if supported_features & value } supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES, set()) - 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: + if COLOR_MODE_RGB in supported_color_modes: + supported.add("color") # Adding brightness here, see # comment https://github.com/basnijholt/adaptive-lighting/issues/112#issuecomment-836944011 - 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 + supported.add("brightness") + if COLOR_MODE_RGBW in supported_color_modes: + supported.add("color") + supported.add("brightness") # see above url + if COLOR_MODE_RGBWW: + 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 def color_difference_redmean( @@ -1122,13 +1088,13 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): # Build service data. service_data = {ATTR_ENTITY_ID: light} - features, supports_colors = _supported_features(self.hass, light) + features = _supported_features(self.hass, light) # Check transition == 0 to fix #378 - use_transition = ATTR_TRANSITION in features and transition > 0 + use_transition = "transition" in features and transition > 0 if use_transition: service_data[ATTR_TRANSITION] = transition - if ATTR_BRIGHTNESS in features and adapt_brightness: + if "brightness" in features and adapt_brightness: brightness = round(255 * self._settings["brightness_pct"] / 100) service_data[ATTR_BRIGHTNESS] = brightness @@ -1137,18 +1103,19 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): and self._sun_light_settings.sleep_rgb_or_color_temp == "rgb_color" ) if ( - ATTR_COLOR_TEMP_KELVIN in features + "color_temp" in features and adapt_color - and not (prefer_rgb_color and supports_colors) - and not (sleep_rgb and supports_colors) + and not (prefer_rgb_color and "color" in features) + and not (sleep_rgb and "color" in features) ): _LOGGER.debug("%s: Setting color_temp of light %s", self._name, light) - min_kelvin = features[ATTR_MIN_COLOR_TEMP_KELVIN] - max_kelvin = features[ATTR_MAX_COLOR_TEMP_KELVIN] + attributes = self.hass.states.get(light).attributes + min_kelvin = attributes["min_color_temp_kelvin"] + max_kelvin = attributes["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 supports_colors and adapt_color: + elif "color" in features and adapt_color: _LOGGER.debug("%s: Setting rgb_color of light %s", self._name, light) service_data[ATTR_RGB_COLOR] = self._settings["rgb_color"] diff --git a/tests/test_switch.py b/tests/test_switch.py index 4ac92c64..2136b707 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -3,23 +3,18 @@ import asyncio from copy import deepcopy import datetime -import itertools import logging from random import randint from typing import Any -from unittest.mock import MagicMock, Mock, patch +from unittest.mock import Mock, patch from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_COLOR_TEMP_KELVIN, - ATTR_MAX_COLOR_TEMP_KELVIN, - ATTR_MIN_COLOR_TEMP_KELVIN, ATTR_RGB_COLOR, - ATTR_SUPPORTED_COLOR_MODES, ATTR_TRANSITION, ATTR_XY_COLOR, - COLOR_MODE_BRIGHTNESS, ) from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import SERVICE_TURN_OFF @@ -75,7 +70,6 @@ from custom_components.adaptive_lighting.const import ( CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, - CONST_COLOR, DEFAULT_MAX_BRIGHTNESS, DEFAULT_NAME, DEFAULT_SLEEP_BRIGHTNESS, @@ -88,12 +82,9 @@ from custom_components.adaptive_lighting.const import ( UNDO_UPDATE_LISTENER, ) from custom_components.adaptive_lighting.switch import ( - _SUPPORT_OPTS, INTERNAL_CONF_PROACTIVE_SERVICE_CALL_ADAPTATION, - VALID_COLOR_MODES, AdaptiveSwitch, _attributes_have_changed, - _supported_features, color_difference_redmean, create_context, is_our_context, @@ -559,79 +550,6 @@ async def test_turn_on_off_listener_not_tracking_untracked_lights(hass): assert light not in switch.turn_on_off_listener.lights -def test_supported_features(hass): # noqa: C901 - """Test the supported features of a light.""" - - possible_legacy_features = {} - MAX_COMBINATIONS = 4 # maximum number of elements that can be combined - for i in range(1, min(MAX_COMBINATIONS, len(_SUPPORT_OPTS)) + 1): - for combination in itertools.combinations(_SUPPORT_OPTS.keys(), i): - key = "_".join(combination) - value = [v for k, v in _SUPPORT_OPTS.items() if k in combination] - possible_legacy_features[key] = value - - possible_color_modes = {} - for i in range(1, len(VALID_COLOR_MODES) + 1): - for combination in itertools.combinations(VALID_COLOR_MODES.keys(), i): - key = "_".join(combination) - value = [v for k, v in VALID_COLOR_MODES.items() if k in combination] - possible_color_modes[key] = value - - # create a mock HomeAssistant object - hass = MagicMock() - - # iterate over possible legacy features - for feature_key, feature_values in possible_legacy_features.items(): - # _LOGGER.debug(feature_values) - # set the attributes of the mock state object to the possible legacy feature values - state_attrs = {ATTR_SUPPORTED_FEATURES: sum(feature_values)} - hass.states.get.return_value.attributes = state_attrs - - # iterate over possible color modes - for mode_key, mode_values in possible_color_modes.items(): - # _LOGGER.debug(mode_values) - # set the attributes of the mock state object to the possible color mode values - state_attrs[ATTR_SUPPORTED_COLOR_MODES] = set(mode_values) - hass.states.get.return_value.attributes = state_attrs - - # Handle both the new and the old _supported_features. - result = _supported_features(hass, ENTITY_LIGHT) - supported, supports_colors = ( - result if isinstance(result, tuple) else (result, None) - ) - expected_supported = {} if supports_colors is not None else set() - for mode, attr in VALID_COLOR_MODES.items(): - if mode in mode_values: - if supports_colors is None: - expected_supported.add(mode) - else: - expected_supported[attr] = True - if supports_colors is True: - expected_supported[COLOR_MODE_BRIGHTNESS] = True - for opt, value in _SUPPORT_OPTS.items(): - if value in feature_values: - if supports_colors is None: - expected_supported.add(opt) - else: - if supports_colors is True: - expected_supported[COLOR_MODE_BRIGHTNESS] = True - if opt in VALID_COLOR_MODES: - expected_supported[VALID_COLOR_MODES[opt]] = True - elif opt != CONST_COLOR: - expected_supported[opt] = True - if ATTR_MIN_COLOR_TEMP_KELVIN in supported: - supported.pop(ATTR_MIN_COLOR_TEMP_KELVIN) - if ATTR_MAX_COLOR_TEMP_KELVIN in supported: - supported.pop(ATTR_MAX_COLOR_TEMP_KELVIN) - assert supported == expected_supported, ( - f"\nExpected supported: {expected_supported}\n" - f"Actual supported: {supported}\n" - f"feature_values: {feature_values}\n" - f"mode_values: {mode_values}\n" - f"supports_colors: {supports_colors}\n" - ) - - @pytest.mark.dependency(depends=GLOBAL_TEST_DEPENDENCIES) async def test_manual_control(hass): """Test the 'manual control' tracking."""