mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
Add support for light color modes (#47720)
* Add support for light color modes * Update tests * Update comments * Fix bugs, add tests * Suppress lint errors * Don't suppress brightness when state is ambiguous * Improve reproduce_state + add tests * Add comment * Change COLOR_MODE_* constants, rename COLOR_MODE_DIMMER to COLOR_MODE_BRIGHTNESS * Fix tests * Tweaks
This commit is contained in:
parent
333e6a215a
commit
5f2326fb57
@ -6,7 +6,7 @@ import dataclasses
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Dict, List, Optional, Tuple, cast
|
from typing import Dict, List, Optional, Set, Tuple, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -38,19 +38,49 @@ DATA_PROFILES = "light_profiles"
|
|||||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
# Bitfield of features supported by the light entity
|
# Bitfield of features supported by the light entity
|
||||||
SUPPORT_BRIGHTNESS = 1
|
SUPPORT_BRIGHTNESS = 1 # Deprecated, replaced by color modes
|
||||||
SUPPORT_COLOR_TEMP = 2
|
SUPPORT_COLOR_TEMP = 2 # Deprecated, replaced by color modes
|
||||||
SUPPORT_EFFECT = 4
|
SUPPORT_EFFECT = 4
|
||||||
SUPPORT_FLASH = 8
|
SUPPORT_FLASH = 8
|
||||||
SUPPORT_COLOR = 16
|
SUPPORT_COLOR = 16 # Deprecated, replaced by color modes
|
||||||
SUPPORT_TRANSITION = 32
|
SUPPORT_TRANSITION = 32
|
||||||
SUPPORT_WHITE_VALUE = 128
|
SUPPORT_WHITE_VALUE = 128 # Deprecated, replaced by color modes
|
||||||
|
|
||||||
|
# Color mode of the light
|
||||||
|
ATTR_COLOR_MODE = "color_mode"
|
||||||
|
# List of color modes supported by the light
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES = "supported_color_modes"
|
||||||
|
# Possible color modes
|
||||||
|
COLOR_MODE_UNKNOWN = "unknown" # Ambiguous color mode
|
||||||
|
COLOR_MODE_ONOFF = "onoff" # Must be the only supported mode
|
||||||
|
COLOR_MODE_BRIGHTNESS = "brightness" # Must be the only supported mode
|
||||||
|
COLOR_MODE_COLOR_TEMP = "color_temp"
|
||||||
|
COLOR_MODE_HS = "hs"
|
||||||
|
COLOR_MODE_XY = "xy"
|
||||||
|
COLOR_MODE_RGB = "rgb"
|
||||||
|
COLOR_MODE_RGBW = "rgbw"
|
||||||
|
COLOR_MODE_RGBWW = "rgbww"
|
||||||
|
|
||||||
|
VALID_COLOR_MODES = {
|
||||||
|
COLOR_MODE_ONOFF,
|
||||||
|
COLOR_MODE_BRIGHTNESS,
|
||||||
|
COLOR_MODE_COLOR_TEMP,
|
||||||
|
COLOR_MODE_HS,
|
||||||
|
COLOR_MODE_XY,
|
||||||
|
COLOR_MODE_RGB,
|
||||||
|
COLOR_MODE_RGBW,
|
||||||
|
COLOR_MODE_RGBWW,
|
||||||
|
}
|
||||||
|
COLOR_MODES_BRIGHTNESS = VALID_COLOR_MODES - {COLOR_MODE_ONOFF}
|
||||||
|
COLOR_MODES_COLOR = {COLOR_MODE_HS, COLOR_MODE_RGB, COLOR_MODE_XY}
|
||||||
|
|
||||||
# Float that represents transition time in seconds to make change.
|
# Float that represents transition time in seconds to make change.
|
||||||
ATTR_TRANSITION = "transition"
|
ATTR_TRANSITION = "transition"
|
||||||
|
|
||||||
# Lists holding color values
|
# Lists holding color values
|
||||||
ATTR_RGB_COLOR = "rgb_color"
|
ATTR_RGB_COLOR = "rgb_color"
|
||||||
|
ATTR_RGBW_COLOR = "rgbw_color"
|
||||||
|
ATTR_RGBWW_COLOR = "rgbww_color"
|
||||||
ATTR_XY_COLOR = "xy_color"
|
ATTR_XY_COLOR = "xy_color"
|
||||||
ATTR_HS_COLOR = "hs_color"
|
ATTR_HS_COLOR = "hs_color"
|
||||||
ATTR_COLOR_TEMP = "color_temp"
|
ATTR_COLOR_TEMP = "color_temp"
|
||||||
@ -104,7 +134,13 @@ LIGHT_TURN_ON_SCHEMA = {
|
|||||||
vol.Exclusive(ATTR_BRIGHTNESS_STEP_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_STEP_PCT,
|
vol.Exclusive(ATTR_BRIGHTNESS_STEP_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_STEP_PCT,
|
||||||
vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string,
|
vol.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string,
|
||||||
vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All(
|
vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All(
|
||||||
vol.ExactSequence((cv.byte, cv.byte, cv.byte)), vol.Coerce(tuple)
|
vol.ExactSequence((cv.byte,) * 3), vol.Coerce(tuple)
|
||||||
|
),
|
||||||
|
vol.Exclusive(ATTR_RGBW_COLOR, COLOR_GROUP): vol.All(
|
||||||
|
vol.ExactSequence((cv.byte,) * 4), vol.Coerce(tuple)
|
||||||
|
),
|
||||||
|
vol.Exclusive(ATTR_RGBWW_COLOR, COLOR_GROUP): vol.All(
|
||||||
|
vol.ExactSequence((cv.byte,) * 5), vol.Coerce(tuple)
|
||||||
),
|
),
|
||||||
vol.Exclusive(ATTR_XY_COLOR, COLOR_GROUP): vol.All(
|
vol.Exclusive(ATTR_XY_COLOR, COLOR_GROUP): vol.All(
|
||||||
vol.ExactSequence((cv.small_float, cv.small_float)), vol.Coerce(tuple)
|
vol.ExactSequence((cv.small_float, cv.small_float)), vol.Coerce(tuple)
|
||||||
@ -166,14 +202,6 @@ def preprocess_turn_on_alternatives(hass, params):
|
|||||||
if brightness_pct is not None:
|
if brightness_pct is not None:
|
||||||
params[ATTR_BRIGHTNESS] = round(255 * brightness_pct / 100)
|
params[ATTR_BRIGHTNESS] = round(255 * brightness_pct / 100)
|
||||||
|
|
||||||
xy_color = params.pop(ATTR_XY_COLOR, None)
|
|
||||||
if xy_color is not None:
|
|
||||||
params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color)
|
|
||||||
|
|
||||||
rgb_color = params.pop(ATTR_RGB_COLOR, None)
|
|
||||||
if rgb_color is not None:
|
|
||||||
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
|
||||||
|
|
||||||
|
|
||||||
def filter_turn_off_params(params):
|
def filter_turn_off_params(params):
|
||||||
"""Filter out params not used in turn off."""
|
"""Filter out params not used in turn off."""
|
||||||
@ -228,6 +256,52 @@ async def async_setup(hass, config):
|
|||||||
if ATTR_PROFILE not in params:
|
if ATTR_PROFILE not in params:
|
||||||
profiles.apply_default(light.entity_id, params)
|
profiles.apply_default(light.entity_id, params)
|
||||||
|
|
||||||
|
supported_color_modes = light.supported_color_modes
|
||||||
|
# Backwards compatibility: if an RGBWW color is specified, convert to RGB + W
|
||||||
|
# for legacy lights
|
||||||
|
if ATTR_RGBW_COLOR in params:
|
||||||
|
legacy_supported_color_modes = (
|
||||||
|
light._light_internal_supported_color_modes # pylint: disable=protected-access
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
COLOR_MODE_RGBW in legacy_supported_color_modes
|
||||||
|
and not supported_color_modes
|
||||||
|
):
|
||||||
|
rgbw_color = params.pop(ATTR_RGBW_COLOR)
|
||||||
|
params[ATTR_RGB_COLOR] = rgbw_color[0:3]
|
||||||
|
params[ATTR_WHITE_VALUE] = rgbw_color[3]
|
||||||
|
|
||||||
|
# If a color is specified, convert to the color space supported by the light
|
||||||
|
# Backwards compatibility: Fall back to hs color if light.supported_color_modes
|
||||||
|
# is not implemented
|
||||||
|
if not supported_color_modes:
|
||||||
|
if (rgb_color := params.pop(ATTR_RGB_COLOR, None)) is not None:
|
||||||
|
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||||
|
elif (xy_color := params.pop(ATTR_XY_COLOR, None)) is not None:
|
||||||
|
params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color)
|
||||||
|
elif ATTR_HS_COLOR in params and COLOR_MODE_HS not in supported_color_modes:
|
||||||
|
hs_color = params.pop(ATTR_HS_COLOR)
|
||||||
|
if COLOR_MODE_RGB in supported_color_modes:
|
||||||
|
params[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color)
|
||||||
|
elif COLOR_MODE_XY in supported_color_modes:
|
||||||
|
params[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
|
||||||
|
elif ATTR_RGB_COLOR in params and COLOR_MODE_RGB not in supported_color_modes:
|
||||||
|
rgb_color = params.pop(ATTR_RGB_COLOR)
|
||||||
|
if COLOR_MODE_HS in supported_color_modes:
|
||||||
|
params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||||
|
elif COLOR_MODE_XY in supported_color_modes:
|
||||||
|
params[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color)
|
||||||
|
elif ATTR_XY_COLOR in params and COLOR_MODE_XY not in supported_color_modes:
|
||||||
|
xy_color = params.pop(ATTR_XY_COLOR)
|
||||||
|
if COLOR_MODE_HS in supported_color_modes:
|
||||||
|
params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color)
|
||||||
|
elif COLOR_MODE_RGB in supported_color_modes:
|
||||||
|
params[ATTR_RGB_COLOR] = color_util.color_xy_to_RGB(*xy_color)
|
||||||
|
|
||||||
|
# Remove deprecated white value if the light supports color mode
|
||||||
|
if supported_color_modes:
|
||||||
|
params.pop(ATTR_WHITE_VALUE, None)
|
||||||
|
|
||||||
# Zero brightness: Light will be turned off
|
# Zero brightness: Light will be turned off
|
||||||
if params.get(ATTR_BRIGHTNESS) == 0:
|
if params.get(ATTR_BRIGHTNESS) == 0:
|
||||||
await light.async_turn_off(**filter_turn_off_params(params))
|
await light.async_turn_off(**filter_turn_off_params(params))
|
||||||
@ -411,11 +485,83 @@ class LightEntity(ToggleEntity):
|
|||||||
"""Return the brightness of this light between 0..255."""
|
"""Return the brightness of this light between 0..255."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_mode(self) -> Optional[str]:
|
||||||
|
"""Return the color mode of the light."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _light_internal_color_mode(self) -> str:
|
||||||
|
"""Return the color mode of the light with backwards compatibility."""
|
||||||
|
color_mode = self.color_mode
|
||||||
|
|
||||||
|
if color_mode is None:
|
||||||
|
# Backwards compatibility for color_mode added in 2021.4
|
||||||
|
# Add warning in 2021.6, remove in 2021.10
|
||||||
|
supported = self._light_internal_supported_color_modes
|
||||||
|
|
||||||
|
if (
|
||||||
|
COLOR_MODE_RGBW in supported
|
||||||
|
and self.white_value is not None
|
||||||
|
and self.hs_color is not None
|
||||||
|
):
|
||||||
|
return COLOR_MODE_RGBW
|
||||||
|
if COLOR_MODE_HS in supported and self.hs_color is not None:
|
||||||
|
return COLOR_MODE_HS
|
||||||
|
if COLOR_MODE_COLOR_TEMP in supported and self.color_temp is not None:
|
||||||
|
return COLOR_MODE_COLOR_TEMP
|
||||||
|
if COLOR_MODE_BRIGHTNESS in supported and self.brightness is not None:
|
||||||
|
return COLOR_MODE_BRIGHTNESS
|
||||||
|
if COLOR_MODE_ONOFF in supported:
|
||||||
|
return COLOR_MODE_ONOFF
|
||||||
|
return COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
return color_mode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hs_color(self) -> Optional[Tuple[float, float]]:
|
def hs_color(self) -> Optional[Tuple[float, float]]:
|
||||||
"""Return the hue and saturation color value [float, float]."""
|
"""Return the hue and saturation color value [float, float]."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xy_color(self) -> Optional[Tuple[float, float]]:
|
||||||
|
"""Return the xy color value [float, float]."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgb_color(self) -> Optional[Tuple[int, int, int]]:
|
||||||
|
"""Return the rgb color value [int, int, int]."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgbw_color(self) -> Optional[Tuple[int, int, int, int]]:
|
||||||
|
"""Return the rgbw color value [int, int, int, int]."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _light_internal_rgbw_color(self) -> Optional[Tuple[int, int, int, int]]:
|
||||||
|
"""Return the rgbw color value [int, int, int, int]."""
|
||||||
|
rgbw_color = self.rgbw_color
|
||||||
|
if (
|
||||||
|
rgbw_color is None
|
||||||
|
and self.hs_color is not None
|
||||||
|
and self.white_value is not None
|
||||||
|
):
|
||||||
|
# Backwards compatibility for rgbw_color added in 2021.4
|
||||||
|
# Add warning in 2021.6, remove in 2021.10
|
||||||
|
r, g, b = color_util.color_hs_to_RGB( # pylint: disable=invalid-name
|
||||||
|
*self.hs_color
|
||||||
|
)
|
||||||
|
w = self.white_value # pylint: disable=invalid-name
|
||||||
|
rgbw_color = (r, g, b, w)
|
||||||
|
|
||||||
|
return rgbw_color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgbww_color(self) -> Optional[Tuple[int, int, int, int, int]]:
|
||||||
|
"""Return the rgbww color value [int, int, int, int, int]."""
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color_temp(self) -> Optional[int]:
|
def color_temp(self) -> Optional[int]:
|
||||||
"""Return the CT color value in mireds."""
|
"""Return the CT color value in mireds."""
|
||||||
@ -463,6 +609,29 @@ class LightEntity(ToggleEntity):
|
|||||||
if supported_features & SUPPORT_EFFECT:
|
if supported_features & SUPPORT_EFFECT:
|
||||||
data[ATTR_EFFECT_LIST] = self.effect_list
|
data[ATTR_EFFECT_LIST] = self.effect_list
|
||||||
|
|
||||||
|
data[ATTR_SUPPORTED_COLOR_MODES] = sorted(
|
||||||
|
list(self._light_internal_supported_color_modes)
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _light_internal_convert_color(self, color_mode: str) -> dict:
|
||||||
|
data: Dict[str, Tuple] = {}
|
||||||
|
if color_mode == COLOR_MODE_HS and self.hs_color:
|
||||||
|
hs_color = self.hs_color
|
||||||
|
data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3))
|
||||||
|
data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color)
|
||||||
|
data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
|
||||||
|
elif color_mode == COLOR_MODE_XY and self.xy_color:
|
||||||
|
xy_color = self.xy_color
|
||||||
|
data[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color)
|
||||||
|
data[ATTR_RGB_COLOR] = color_util.color_xy_to_RGB(*xy_color)
|
||||||
|
data[ATTR_XY_COLOR] = (round(xy_color[0], 6), round(xy_color[1], 6))
|
||||||
|
elif color_mode == COLOR_MODE_RGB and self.rgb_color:
|
||||||
|
rgb_color = self.rgb_color
|
||||||
|
data[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
|
||||||
|
data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3])
|
||||||
|
data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -473,27 +642,85 @@ class LightEntity(ToggleEntity):
|
|||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
supported_features = self.supported_features
|
supported_features = self.supported_features
|
||||||
|
color_mode = self._light_internal_color_mode
|
||||||
|
|
||||||
if supported_features & SUPPORT_BRIGHTNESS:
|
if color_mode not in self._light_internal_supported_color_modes:
|
||||||
|
# Increase severity to warning in 2021.6, reject in 2021.10
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s: set to unsupported color_mode: %s, supported_color_modes: %s",
|
||||||
|
self.entity_id,
|
||||||
|
color_mode,
|
||||||
|
self._light_internal_supported_color_modes,
|
||||||
|
)
|
||||||
|
|
||||||
|
data[ATTR_COLOR_MODE] = color_mode
|
||||||
|
|
||||||
|
if color_mode in COLOR_MODES_BRIGHTNESS:
|
||||||
|
data[ATTR_BRIGHTNESS] = self.brightness
|
||||||
|
elif supported_features & SUPPORT_BRIGHTNESS:
|
||||||
|
# Backwards compatibility for ambiguous / incomplete states
|
||||||
|
# Add warning in 2021.6, remove in 2021.10
|
||||||
data[ATTR_BRIGHTNESS] = self.brightness
|
data[ATTR_BRIGHTNESS] = self.brightness
|
||||||
|
|
||||||
if supported_features & SUPPORT_COLOR_TEMP:
|
if color_mode == COLOR_MODE_COLOR_TEMP:
|
||||||
data[ATTR_COLOR_TEMP] = self.color_temp
|
data[ATTR_COLOR_TEMP] = self.color_temp
|
||||||
|
|
||||||
if supported_features & SUPPORT_COLOR and self.hs_color:
|
if color_mode in COLOR_MODES_COLOR:
|
||||||
hs_color = self.hs_color
|
data.update(self._light_internal_convert_color(color_mode))
|
||||||
data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3))
|
|
||||||
data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color)
|
|
||||||
data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color)
|
|
||||||
|
|
||||||
if supported_features & SUPPORT_WHITE_VALUE:
|
if color_mode == COLOR_MODE_RGBW:
|
||||||
|
data[ATTR_RGBW_COLOR] = self._light_internal_rgbw_color
|
||||||
|
|
||||||
|
if color_mode == COLOR_MODE_RGBWW:
|
||||||
|
data[ATTR_RGBWW_COLOR] = self.rgbww_color
|
||||||
|
|
||||||
|
if supported_features & SUPPORT_COLOR_TEMP and not self.supported_color_modes:
|
||||||
|
# Backwards compatibility
|
||||||
|
# Add warning in 2021.6, remove in 2021.10
|
||||||
|
data[ATTR_COLOR_TEMP] = self.color_temp
|
||||||
|
|
||||||
|
if supported_features & SUPPORT_WHITE_VALUE and not self.supported_color_modes:
|
||||||
|
# Backwards compatibility
|
||||||
|
# Add warning in 2021.6, remove in 2021.10
|
||||||
data[ATTR_WHITE_VALUE] = self.white_value
|
data[ATTR_WHITE_VALUE] = self.white_value
|
||||||
|
if self.hs_color is not None:
|
||||||
|
data.update(self._light_internal_convert_color(COLOR_MODE_HS))
|
||||||
|
|
||||||
if supported_features & SUPPORT_EFFECT:
|
if supported_features & SUPPORT_EFFECT:
|
||||||
data[ATTR_EFFECT] = self.effect
|
data[ATTR_EFFECT] = self.effect
|
||||||
|
|
||||||
return {key: val for key, val in data.items() if val is not None}
|
return {key: val for key, val in data.items() if val is not None}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _light_internal_supported_color_modes(self) -> Set:
|
||||||
|
"""Calculate supported color modes with backwards compatibility."""
|
||||||
|
supported_color_modes = self.supported_color_modes
|
||||||
|
|
||||||
|
if supported_color_modes is None:
|
||||||
|
# Backwards compatibility for supported_color_modes added in 2021.4
|
||||||
|
# Add warning in 2021.6, remove in 2021.10
|
||||||
|
supported_features = self.supported_features
|
||||||
|
supported_color_modes = set()
|
||||||
|
|
||||||
|
if supported_features & SUPPORT_COLOR_TEMP:
|
||||||
|
supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
||||||
|
if supported_features & SUPPORT_COLOR:
|
||||||
|
supported_color_modes.add(COLOR_MODE_HS)
|
||||||
|
if supported_features & SUPPORT_WHITE_VALUE:
|
||||||
|
supported_color_modes.add(COLOR_MODE_RGBW)
|
||||||
|
if supported_features & SUPPORT_BRIGHTNESS and not supported_color_modes:
|
||||||
|
supported_color_modes = {COLOR_MODE_BRIGHTNESS}
|
||||||
|
|
||||||
|
if not supported_color_modes:
|
||||||
|
supported_color_modes = {COLOR_MODE_ONOFF}
|
||||||
|
|
||||||
|
return supported_color_modes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_color_modes(self) -> Optional[Set]:
|
||||||
|
"""Flag supported color modes."""
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
|
@ -17,6 +17,7 @@ from homeassistant.helpers.typing import HomeAssistantType
|
|||||||
from . import (
|
from . import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_BRIGHTNESS_PCT,
|
ATTR_BRIGHTNESS_PCT,
|
||||||
|
ATTR_COLOR_MODE,
|
||||||
ATTR_COLOR_NAME,
|
ATTR_COLOR_NAME,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_EFFECT,
|
ATTR_EFFECT,
|
||||||
@ -25,9 +26,18 @@ from . import (
|
|||||||
ATTR_KELVIN,
|
ATTR_KELVIN,
|
||||||
ATTR_PROFILE,
|
ATTR_PROFILE,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_RGBWW_COLOR,
|
||||||
ATTR_TRANSITION,
|
ATTR_TRANSITION,
|
||||||
ATTR_WHITE_VALUE,
|
ATTR_WHITE_VALUE,
|
||||||
ATTR_XY_COLOR,
|
ATTR_XY_COLOR,
|
||||||
|
COLOR_MODE_COLOR_TEMP,
|
||||||
|
COLOR_MODE_HS,
|
||||||
|
COLOR_MODE_RGB,
|
||||||
|
COLOR_MODE_RGBW,
|
||||||
|
COLOR_MODE_RGBWW,
|
||||||
|
COLOR_MODE_UNKNOWN,
|
||||||
|
COLOR_MODE_XY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,6 +58,8 @@ COLOR_GROUP = [
|
|||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_RGBWW_COLOR,
|
||||||
ATTR_XY_COLOR,
|
ATTR_XY_COLOR,
|
||||||
# The following color attributes are deprecated
|
# The following color attributes are deprecated
|
||||||
ATTR_PROFILE,
|
ATTR_PROFILE,
|
||||||
@ -55,6 +67,15 @@ COLOR_GROUP = [
|
|||||||
ATTR_KELVIN,
|
ATTR_KELVIN,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
COLOR_MODE_TO_ATTRIBUTE = {
|
||||||
|
COLOR_MODE_COLOR_TEMP: ATTR_COLOR_TEMP,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
DEPRECATED_GROUP = [
|
DEPRECATED_GROUP = [
|
||||||
ATTR_BRIGHTNESS_PCT,
|
ATTR_BRIGHTNESS_PCT,
|
||||||
ATTR_COLOR_NAME,
|
ATTR_COLOR_NAME,
|
||||||
@ -114,11 +135,29 @@ async def _async_reproduce_state(
|
|||||||
if attr in state.attributes:
|
if attr in state.attributes:
|
||||||
service_data[attr] = state.attributes[attr]
|
service_data[attr] = state.attributes[attr]
|
||||||
|
|
||||||
for color_attr in COLOR_GROUP:
|
if (
|
||||||
# Choose the first color that is specified
|
state.attributes.get(ATTR_COLOR_MODE, COLOR_MODE_UNKNOWN)
|
||||||
if color_attr in state.attributes:
|
!= COLOR_MODE_UNKNOWN
|
||||||
|
):
|
||||||
|
# Remove deprecated white value if we got a valid color mode
|
||||||
|
service_data.pop(ATTR_WHITE_VALUE, None)
|
||||||
|
color_mode = state.attributes[ATTR_COLOR_MODE]
|
||||||
|
if color_attr := COLOR_MODE_TO_ATTRIBUTE.get(color_mode):
|
||||||
|
if color_attr not in state.attributes:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Color mode %s specified but attribute %s missing for: %s",
|
||||||
|
color_mode,
|
||||||
|
color_attr,
|
||||||
|
state.entity_id,
|
||||||
|
)
|
||||||
|
return
|
||||||
service_data[color_attr] = state.attributes[color_attr]
|
service_data[color_attr] = state.attributes[color_attr]
|
||||||
break
|
else:
|
||||||
|
# Fall back to Choosing the first color that is specified
|
||||||
|
for color_attr in COLOR_GROUP:
|
||||||
|
if color_attr in state.attributes:
|
||||||
|
service_data[color_attr] = state.attributes[color_attr]
|
||||||
|
break
|
||||||
|
|
||||||
elif state.state == STATE_OFF:
|
elif state.state == STATE_OFF:
|
||||||
service = SERVICE_TURN_OFF
|
service = SERVICE_TURN_OFF
|
||||||
|
@ -8,10 +8,15 @@ from homeassistant import setup
|
|||||||
from homeassistant.components.kulersky.light import DOMAIN
|
from homeassistant.components.kulersky.light import DOMAIN
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
|
ATTR_COLOR_MODE,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
ATTR_WHITE_VALUE,
|
ATTR_WHITE_VALUE,
|
||||||
ATTR_XY_COLOR,
|
ATTR_XY_COLOR,
|
||||||
|
COLOR_MODE_HS,
|
||||||
|
COLOR_MODE_RGBW,
|
||||||
SCAN_INTERVAL,
|
SCAN_INTERVAL,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
SUPPORT_COLOR,
|
SUPPORT_COLOR,
|
||||||
@ -65,6 +70,7 @@ async def test_init(hass, mock_light):
|
|||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "Bedroom",
|
ATTR_FRIENDLY_NAME: "Bedroom",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
||||||
| SUPPORT_COLOR
|
| SUPPORT_COLOR
|
||||||
| SUPPORT_WHITE_VALUE,
|
| SUPPORT_WHITE_VALUE,
|
||||||
@ -168,6 +174,7 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "Bedroom",
|
ATTR_FRIENDLY_NAME: "Bedroom",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
||||||
| SUPPORT_COLOR
|
| SUPPORT_COLOR
|
||||||
| SUPPORT_WHITE_VALUE,
|
| SUPPORT_WHITE_VALUE,
|
||||||
@ -183,6 +190,7 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "Bedroom",
|
ATTR_FRIENDLY_NAME: "Bedroom",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
||||||
| SUPPORT_COLOR
|
| SUPPORT_COLOR
|
||||||
| SUPPORT_WHITE_VALUE,
|
| SUPPORT_WHITE_VALUE,
|
||||||
@ -198,12 +206,15 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "Bedroom",
|
ATTR_FRIENDLY_NAME: "Bedroom",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
|
||||||
| SUPPORT_COLOR
|
| SUPPORT_COLOR
|
||||||
| SUPPORT_WHITE_VALUE,
|
| SUPPORT_WHITE_VALUE,
|
||||||
|
ATTR_COLOR_MODE: COLOR_MODE_RGBW,
|
||||||
ATTR_BRIGHTNESS: 200,
|
ATTR_BRIGHTNESS: 200,
|
||||||
ATTR_HS_COLOR: (200, 60),
|
ATTR_HS_COLOR: (200, 60),
|
||||||
ATTR_RGB_COLOR: (102, 203, 255),
|
ATTR_RGB_COLOR: (102, 203, 255),
|
||||||
|
ATTR_RGBW_COLOR: (102, 203, 255, 240),
|
||||||
ATTR_WHITE_VALUE: 240,
|
ATTR_WHITE_VALUE: 240,
|
||||||
ATTR_XY_COLOR: (0.184, 0.261),
|
ATTR_XY_COLOR: (0.184, 0.261),
|
||||||
}
|
}
|
||||||
|
@ -915,3 +915,482 @@ invalid_no_brightness_no_color_no_transition,,,
|
|||||||
"invalid_no_brightness_no_color_no_transition",
|
"invalid_no_brightness_no_color_no_transition",
|
||||||
):
|
):
|
||||||
assert invalid_profile_name not in profiles.data
|
assert invalid_profile_name not in profiles.data
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("light_state", (STATE_ON, STATE_OFF))
|
||||||
|
async def test_light_backwards_compatibility_supported_color_modes(hass, light_state):
|
||||||
|
"""Test supported_color_modes if not implemented by the entity."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_0", light_state))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_1", light_state))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_2", light_state))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_3", light_state))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_4", light_state))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_5", light_state))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_6", light_state))
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
|
||||||
|
entity1 = platform.ENTITIES[1]
|
||||||
|
entity1.supported_features = light.SUPPORT_BRIGHTNESS
|
||||||
|
|
||||||
|
entity2 = platform.ENTITIES[2]
|
||||||
|
entity2.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR_TEMP
|
||||||
|
|
||||||
|
entity3 = platform.ENTITIES[3]
|
||||||
|
entity3.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR
|
||||||
|
|
||||||
|
entity4 = platform.ENTITIES[4]
|
||||||
|
entity4.supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
entity5 = platform.ENTITIES[5]
|
||||||
|
entity5.supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP
|
||||||
|
)
|
||||||
|
|
||||||
|
entity6 = platform.ENTITIES[6]
|
||||||
|
entity6.supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS
|
||||||
|
| light.SUPPORT_COLOR
|
||||||
|
| light.SUPPORT_COLOR_TEMP
|
||||||
|
| light.SUPPORT_WHITE_VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity0.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_ONOFF]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_ONOFF
|
||||||
|
|
||||||
|
state = hass.states.get(entity1.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_BRIGHTNESS]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
state = hass.states.get(entity2.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_COLOR_TEMP]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
state = hass.states.get(entity3.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
state = hass.states.get(entity4.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_RGBW,
|
||||||
|
]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
state = hass.states.get(entity5.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_COLOR_TEMP,
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
state = hass.states.get(entity6.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_COLOR_TEMP,
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_RGBW,
|
||||||
|
]
|
||||||
|
if light_state == STATE_OFF:
|
||||||
|
assert "color_mode" not in state.attributes
|
||||||
|
else:
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_backwards_compatibility_color_mode(hass):
|
||||||
|
"""Test color_mode if not implemented by the entity."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_0", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_1", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_2", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_3", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_4", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_5", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_6", STATE_ON))
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
|
||||||
|
entity1 = platform.ENTITIES[1]
|
||||||
|
entity1.supported_features = light.SUPPORT_BRIGHTNESS
|
||||||
|
entity1.brightness = 100
|
||||||
|
|
||||||
|
entity2 = platform.ENTITIES[2]
|
||||||
|
entity2.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR_TEMP
|
||||||
|
entity2.color_temp = 100
|
||||||
|
|
||||||
|
entity3 = platform.ENTITIES[3]
|
||||||
|
entity3.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR
|
||||||
|
entity3.hs_color = (240, 100)
|
||||||
|
|
||||||
|
entity4 = platform.ENTITIES[4]
|
||||||
|
entity4.supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE
|
||||||
|
)
|
||||||
|
entity4.hs_color = (240, 100)
|
||||||
|
entity4.white_value = 100
|
||||||
|
|
||||||
|
entity5 = platform.ENTITIES[5]
|
||||||
|
entity5.supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP
|
||||||
|
)
|
||||||
|
entity5.hs_color = (240, 100)
|
||||||
|
entity5.color_temp = 100
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity0.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_ONOFF]
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_ONOFF
|
||||||
|
|
||||||
|
state = hass.states.get(entity1.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_BRIGHTNESS]
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_BRIGHTNESS
|
||||||
|
|
||||||
|
state = hass.states.get(entity2.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_COLOR_TEMP]
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_COLOR_TEMP
|
||||||
|
|
||||||
|
state = hass.states.get(entity3.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS]
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_HS
|
||||||
|
|
||||||
|
state = hass.states.get(entity4.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_RGBW,
|
||||||
|
]
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_RGBW
|
||||||
|
|
||||||
|
state = hass.states.get(entity5.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_COLOR_TEMP,
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
]
|
||||||
|
# hs color prioritized over color_temp, light should report mode COLOR_MODE_HS
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_HS
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_service_call_rgbw(hass):
|
||||||
|
"""Test backwards compatibility for rgbw functionality in service calls."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_legacy_white_value", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON))
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
entity0.supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE
|
||||||
|
)
|
||||||
|
|
||||||
|
entity1 = platform.ENTITIES[1]
|
||||||
|
entity1.supported_color_modes = {light.COLOR_MODE_RGBW}
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity0.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_RGBW,
|
||||||
|
]
|
||||||
|
|
||||||
|
state = hass.states.get(entity1.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGBW]
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": [entity0.entity_id, entity1.entity_id],
|
||||||
|
"brightness_pct": 100,
|
||||||
|
"rgbw_color": (10, 20, 30, 40),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, data = entity0.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "hs_color": (210.0, 66.667), "white_value": 40}
|
||||||
|
_, data = entity1.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "rgbw_color": (10, 20, 30, 40)}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_state_rgbw(hass):
|
||||||
|
"""Test rgbw color conversion in state updates."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_legacy_white_value", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON))
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
legacy_supported_features = (
|
||||||
|
light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE
|
||||||
|
)
|
||||||
|
entity0.supported_features = legacy_supported_features
|
||||||
|
entity0.hs_color = (210.0, 66.667)
|
||||||
|
entity0.rgb_color = "Invalid" # Should be ignored
|
||||||
|
entity0.rgbww_color = "Invalid" # Should be ignored
|
||||||
|
entity0.white_value = 40
|
||||||
|
entity0.xy_color = "Invalid" # Should be ignored
|
||||||
|
|
||||||
|
entity1 = platform.ENTITIES[1]
|
||||||
|
entity1.supported_color_modes = {light.COLOR_MODE_RGBW}
|
||||||
|
entity1.color_mode = light.COLOR_MODE_RGBW
|
||||||
|
entity1.hs_color = "Invalid" # Should be ignored
|
||||||
|
entity1.rgb_color = "Invalid" # Should be ignored
|
||||||
|
entity1.rgbw_color = (1, 2, 3, 4)
|
||||||
|
entity1.rgbww_color = "Invalid" # Should be ignored
|
||||||
|
entity1.white_value = "Invalid" # Should be ignored
|
||||||
|
entity1.xy_color = "Invalid" # Should be ignored
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity0.entity_id)
|
||||||
|
assert state.attributes == {
|
||||||
|
"color_mode": light.COLOR_MODE_RGBW,
|
||||||
|
"friendly_name": "Test_legacy_white_value",
|
||||||
|
"supported_color_modes": [light.COLOR_MODE_HS, light.COLOR_MODE_RGBW],
|
||||||
|
"supported_features": legacy_supported_features,
|
||||||
|
"hs_color": (210.0, 66.667),
|
||||||
|
"rgb_color": (84, 169, 255),
|
||||||
|
"rgbw_color": (84, 169, 255, 40),
|
||||||
|
"white_value": 40,
|
||||||
|
"xy_color": (0.173, 0.207),
|
||||||
|
}
|
||||||
|
|
||||||
|
state = hass.states.get(entity1.entity_id)
|
||||||
|
assert state.attributes == {
|
||||||
|
"color_mode": light.COLOR_MODE_RGBW,
|
||||||
|
"friendly_name": "Test_rgbw",
|
||||||
|
"supported_color_modes": [light.COLOR_MODE_RGBW],
|
||||||
|
"supported_features": 0,
|
||||||
|
"rgbw_color": (1, 2, 3, 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_service_call_color_conversion(hass):
|
||||||
|
"""Test color conversion in service calls."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_rgb", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_xy", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_all", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_legacy", STATE_ON))
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
entity0.supported_color_modes = {light.COLOR_MODE_HS}
|
||||||
|
|
||||||
|
entity1 = platform.ENTITIES[1]
|
||||||
|
entity1.supported_color_modes = {light.COLOR_MODE_RGB}
|
||||||
|
|
||||||
|
entity2 = platform.ENTITIES[2]
|
||||||
|
entity2.supported_color_modes = {light.COLOR_MODE_XY}
|
||||||
|
|
||||||
|
entity3 = platform.ENTITIES[3]
|
||||||
|
entity3.supported_color_modes = {
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_RGB,
|
||||||
|
light.COLOR_MODE_XY,
|
||||||
|
}
|
||||||
|
|
||||||
|
entity4 = platform.ENTITIES[4]
|
||||||
|
entity4.supported_features = light.SUPPORT_COLOR
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity0.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS]
|
||||||
|
|
||||||
|
state = hass.states.get(entity1.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGB]
|
||||||
|
|
||||||
|
state = hass.states.get(entity2.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_XY]
|
||||||
|
|
||||||
|
state = hass.states.get(entity3.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_RGB,
|
||||||
|
light.COLOR_MODE_XY,
|
||||||
|
]
|
||||||
|
|
||||||
|
state = hass.states.get(entity4.entity_id)
|
||||||
|
assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS]
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": [
|
||||||
|
entity0.entity_id,
|
||||||
|
entity1.entity_id,
|
||||||
|
entity2.entity_id,
|
||||||
|
entity3.entity_id,
|
||||||
|
entity4.entity_id,
|
||||||
|
],
|
||||||
|
"brightness_pct": 100,
|
||||||
|
"hs_color": (240, 100),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = entity0.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "hs_color": (240.0, 100.0)}
|
||||||
|
_, data = entity1.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "rgb_color": (0, 0, 255)}
|
||||||
|
_, data = entity2.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "xy_color": (0.136, 0.04)}
|
||||||
|
_, data = entity3.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "hs_color": (240.0, 100.0)}
|
||||||
|
_, data = entity4.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 255, "hs_color": (240.0, 100.0)}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": [
|
||||||
|
entity0.entity_id,
|
||||||
|
entity1.entity_id,
|
||||||
|
entity2.entity_id,
|
||||||
|
entity3.entity_id,
|
||||||
|
entity4.entity_id,
|
||||||
|
],
|
||||||
|
"brightness_pct": 50,
|
||||||
|
"rgb_color": (128, 0, 0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = entity0.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "hs_color": (0.0, 100.0)}
|
||||||
|
_, data = entity1.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "rgb_color": (128, 0, 0)}
|
||||||
|
_, data = entity2.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "xy_color": (0.701, 0.299)}
|
||||||
|
_, data = entity3.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "rgb_color": (128, 0, 0)}
|
||||||
|
_, data = entity4.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "hs_color": (0.0, 100.0)}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": [
|
||||||
|
entity0.entity_id,
|
||||||
|
entity1.entity_id,
|
||||||
|
entity2.entity_id,
|
||||||
|
entity3.entity_id,
|
||||||
|
entity4.entity_id,
|
||||||
|
],
|
||||||
|
"brightness_pct": 50,
|
||||||
|
"xy_color": (0.1, 0.8),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = entity0.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "hs_color": (125.176, 100.0)}
|
||||||
|
_, data = entity1.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "rgb_color": (0, 255, 22)}
|
||||||
|
_, data = entity2.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "xy_color": (0.1, 0.8)}
|
||||||
|
_, data = entity3.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "xy_color": (0.1, 0.8)}
|
||||||
|
_, data = entity4.last_call("turn_on")
|
||||||
|
assert data == {"brightness": 128, "hs_color": (125.176, 100.0)}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_state_color_conversion(hass):
|
||||||
|
"""Test color conversion in state updates."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
platform.init(empty=True)
|
||||||
|
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_rgb", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_xy", STATE_ON))
|
||||||
|
platform.ENTITIES.append(platform.MockLight("Test_legacy", STATE_ON))
|
||||||
|
|
||||||
|
entity0 = platform.ENTITIES[0]
|
||||||
|
entity0.supported_color_modes = {light.COLOR_MODE_HS}
|
||||||
|
entity0.color_mode = light.COLOR_MODE_HS
|
||||||
|
entity0.hs_color = (240, 100)
|
||||||
|
entity0.rgb_color = "Invalid" # Should be ignored
|
||||||
|
entity0.xy_color = "Invalid" # Should be ignored
|
||||||
|
|
||||||
|
entity1 = platform.ENTITIES[1]
|
||||||
|
entity1.supported_color_modes = {light.COLOR_MODE_RGB}
|
||||||
|
entity1.color_mode = light.COLOR_MODE_RGB
|
||||||
|
entity1.hs_color = "Invalid" # Should be ignored
|
||||||
|
entity1.rgb_color = (128, 0, 0)
|
||||||
|
entity1.xy_color = "Invalid" # Should be ignored
|
||||||
|
|
||||||
|
entity2 = platform.ENTITIES[2]
|
||||||
|
entity2.supported_color_modes = {light.COLOR_MODE_XY}
|
||||||
|
entity2.color_mode = light.COLOR_MODE_XY
|
||||||
|
entity2.hs_color = "Invalid" # Should be ignored
|
||||||
|
entity2.rgb_color = "Invalid" # Should be ignored
|
||||||
|
entity2.xy_color = (0.1, 0.8)
|
||||||
|
|
||||||
|
entity3 = platform.ENTITIES[3]
|
||||||
|
entity3.hs_color = (240, 100)
|
||||||
|
entity3.supported_features = light.SUPPORT_COLOR
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity0.entity_id)
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_HS
|
||||||
|
assert state.attributes["hs_color"] == (240, 100)
|
||||||
|
assert state.attributes["rgb_color"] == (0, 0, 255)
|
||||||
|
assert state.attributes["xy_color"] == (0.136, 0.04)
|
||||||
|
|
||||||
|
state = hass.states.get(entity1.entity_id)
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_RGB
|
||||||
|
assert state.attributes["hs_color"] == (0.0, 100.0)
|
||||||
|
assert state.attributes["rgb_color"] == (128, 0, 0)
|
||||||
|
assert state.attributes["xy_color"] == (0.701, 0.299)
|
||||||
|
|
||||||
|
state = hass.states.get(entity2.entity_id)
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_XY
|
||||||
|
assert state.attributes["hs_color"] == (125.176, 100.0)
|
||||||
|
assert state.attributes["rgb_color"] == (0, 255, 22)
|
||||||
|
assert state.attributes["xy_color"] == (0.1, 0.8)
|
||||||
|
|
||||||
|
state = hass.states.get(entity3.entity_id)
|
||||||
|
assert state.attributes["color_mode"] == light.COLOR_MODE_HS
|
||||||
|
assert state.attributes["hs_color"] == (240, 100)
|
||||||
|
assert state.attributes["rgb_color"] == (0, 0, 255)
|
||||||
|
assert state.attributes["xy_color"] == (0.136, 0.04)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
"""Test reproduce state for Light."""
|
"""Test reproduce state for Light."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components import light
|
||||||
from homeassistant.components.light.reproduce_state import DEPRECATION_WARNING
|
from homeassistant.components.light.reproduce_state import DEPRECATION_WARNING
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
|
|
||||||
@ -15,6 +18,8 @@ VALID_HS_COLOR = {"hs_color": (345, 75)}
|
|||||||
VALID_KELVIN = {"kelvin": 4000}
|
VALID_KELVIN = {"kelvin": 4000}
|
||||||
VALID_PROFILE = {"profile": "relax"}
|
VALID_PROFILE = {"profile": "relax"}
|
||||||
VALID_RGB_COLOR = {"rgb_color": (255, 63, 111)}
|
VALID_RGB_COLOR = {"rgb_color": (255, 63, 111)}
|
||||||
|
VALID_RGBW_COLOR = {"rgbw_color": (255, 63, 111, 10)}
|
||||||
|
VALID_RGBWW_COLOR = {"rgbww_color": (255, 63, 111, 10, 20)}
|
||||||
VALID_XY_COLOR = {"xy_color": (0.59, 0.274)}
|
VALID_XY_COLOR = {"xy_color": (0.59, 0.274)}
|
||||||
|
|
||||||
|
|
||||||
@ -91,51 +96,51 @@ async def test_reproducing_states(hass, caplog):
|
|||||||
|
|
||||||
expected_calls = []
|
expected_calls = []
|
||||||
|
|
||||||
expected_off = VALID_BRIGHTNESS
|
expected_off = dict(VALID_BRIGHTNESS)
|
||||||
expected_off["entity_id"] = "light.entity_off"
|
expected_off["entity_id"] = "light.entity_off"
|
||||||
expected_calls.append(expected_off)
|
expected_calls.append(expected_off)
|
||||||
|
|
||||||
expected_bright = VALID_WHITE_VALUE
|
expected_bright = dict(VALID_WHITE_VALUE)
|
||||||
expected_bright["entity_id"] = "light.entity_bright"
|
expected_bright["entity_id"] = "light.entity_bright"
|
||||||
expected_calls.append(expected_bright)
|
expected_calls.append(expected_bright)
|
||||||
|
|
||||||
expected_white = VALID_FLASH
|
expected_white = dict(VALID_FLASH)
|
||||||
expected_white["entity_id"] = "light.entity_white"
|
expected_white["entity_id"] = "light.entity_white"
|
||||||
expected_calls.append(expected_white)
|
expected_calls.append(expected_white)
|
||||||
|
|
||||||
expected_flash = VALID_EFFECT
|
expected_flash = dict(VALID_EFFECT)
|
||||||
expected_flash["entity_id"] = "light.entity_flash"
|
expected_flash["entity_id"] = "light.entity_flash"
|
||||||
expected_calls.append(expected_flash)
|
expected_calls.append(expected_flash)
|
||||||
|
|
||||||
expected_effect = VALID_TRANSITION
|
expected_effect = dict(VALID_TRANSITION)
|
||||||
expected_effect["entity_id"] = "light.entity_effect"
|
expected_effect["entity_id"] = "light.entity_effect"
|
||||||
expected_calls.append(expected_effect)
|
expected_calls.append(expected_effect)
|
||||||
|
|
||||||
expected_trans = VALID_COLOR_NAME
|
expected_trans = dict(VALID_COLOR_NAME)
|
||||||
expected_trans["entity_id"] = "light.entity_trans"
|
expected_trans["entity_id"] = "light.entity_trans"
|
||||||
expected_calls.append(expected_trans)
|
expected_calls.append(expected_trans)
|
||||||
|
|
||||||
expected_name = VALID_COLOR_TEMP
|
expected_name = dict(VALID_COLOR_TEMP)
|
||||||
expected_name["entity_id"] = "light.entity_name"
|
expected_name["entity_id"] = "light.entity_name"
|
||||||
expected_calls.append(expected_name)
|
expected_calls.append(expected_name)
|
||||||
|
|
||||||
expected_temp = VALID_HS_COLOR
|
expected_temp = dict(VALID_HS_COLOR)
|
||||||
expected_temp["entity_id"] = "light.entity_temp"
|
expected_temp["entity_id"] = "light.entity_temp"
|
||||||
expected_calls.append(expected_temp)
|
expected_calls.append(expected_temp)
|
||||||
|
|
||||||
expected_hs = VALID_KELVIN
|
expected_hs = dict(VALID_KELVIN)
|
||||||
expected_hs["entity_id"] = "light.entity_hs"
|
expected_hs["entity_id"] = "light.entity_hs"
|
||||||
expected_calls.append(expected_hs)
|
expected_calls.append(expected_hs)
|
||||||
|
|
||||||
expected_kelvin = VALID_PROFILE
|
expected_kelvin = dict(VALID_PROFILE)
|
||||||
expected_kelvin["entity_id"] = "light.entity_kelvin"
|
expected_kelvin["entity_id"] = "light.entity_kelvin"
|
||||||
expected_calls.append(expected_kelvin)
|
expected_calls.append(expected_kelvin)
|
||||||
|
|
||||||
expected_profile = VALID_RGB_COLOR
|
expected_profile = dict(VALID_RGB_COLOR)
|
||||||
expected_profile["entity_id"] = "light.entity_profile"
|
expected_profile["entity_id"] = "light.entity_profile"
|
||||||
expected_calls.append(expected_profile)
|
expected_calls.append(expected_profile)
|
||||||
|
|
||||||
expected_rgb = VALID_XY_COLOR
|
expected_rgb = dict(VALID_XY_COLOR)
|
||||||
expected_rgb["entity_id"] = "light.entity_rgb"
|
expected_rgb["entity_id"] = "light.entity_rgb"
|
||||||
expected_calls.append(expected_rgb)
|
expected_calls.append(expected_rgb)
|
||||||
|
|
||||||
@ -156,6 +161,59 @@ async def test_reproducing_states(hass, caplog):
|
|||||||
assert turn_off_calls[0].data == {"entity_id": "light.entity_xy"}
|
assert turn_off_calls[0].data == {"entity_id": "light.entity_xy"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"color_mode",
|
||||||
|
(
|
||||||
|
light.COLOR_MODE_COLOR_TEMP,
|
||||||
|
light.COLOR_MODE_BRIGHTNESS,
|
||||||
|
light.COLOR_MODE_HS,
|
||||||
|
light.COLOR_MODE_ONOFF,
|
||||||
|
light.COLOR_MODE_RGB,
|
||||||
|
light.COLOR_MODE_RGBW,
|
||||||
|
light.COLOR_MODE_RGBWW,
|
||||||
|
light.COLOR_MODE_UNKNOWN,
|
||||||
|
light.COLOR_MODE_XY,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_filter_color_modes(hass, caplog, color_mode):
|
||||||
|
"""Test filtering of parameters according to color mode."""
|
||||||
|
hass.states.async_set("light.entity", "off", {})
|
||||||
|
all_colors = {
|
||||||
|
**VALID_WHITE_VALUE,
|
||||||
|
**VALID_COLOR_NAME,
|
||||||
|
**VALID_COLOR_TEMP,
|
||||||
|
**VALID_HS_COLOR,
|
||||||
|
**VALID_KELVIN,
|
||||||
|
**VALID_RGB_COLOR,
|
||||||
|
**VALID_RGBW_COLOR,
|
||||||
|
**VALID_RGBWW_COLOR,
|
||||||
|
**VALID_XY_COLOR,
|
||||||
|
}
|
||||||
|
|
||||||
|
turn_on_calls = async_mock_service(hass, "light", "turn_on")
|
||||||
|
|
||||||
|
await hass.helpers.state.async_reproduce_state(
|
||||||
|
[State("light.entity", "on", {**all_colors, "color_mode": color_mode})]
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_map = {
|
||||||
|
light.COLOR_MODE_COLOR_TEMP: VALID_COLOR_TEMP,
|
||||||
|
light.COLOR_MODE_BRIGHTNESS: {},
|
||||||
|
light.COLOR_MODE_HS: VALID_HS_COLOR,
|
||||||
|
light.COLOR_MODE_ONOFF: {},
|
||||||
|
light.COLOR_MODE_RGB: VALID_RGB_COLOR,
|
||||||
|
light.COLOR_MODE_RGBW: VALID_RGBW_COLOR,
|
||||||
|
light.COLOR_MODE_RGBWW: VALID_RGBWW_COLOR,
|
||||||
|
light.COLOR_MODE_UNKNOWN: {**VALID_HS_COLOR, **VALID_WHITE_VALUE},
|
||||||
|
light.COLOR_MODE_XY: VALID_XY_COLOR,
|
||||||
|
}
|
||||||
|
expected = expected_map[color_mode]
|
||||||
|
|
||||||
|
assert len(turn_on_calls) == 1
|
||||||
|
assert turn_on_calls[0].domain == "light"
|
||||||
|
assert dict(turn_on_calls[0].data) == {"entity_id": "light.entity", **expected}
|
||||||
|
|
||||||
|
|
||||||
async def test_deprecation_warning(hass, caplog):
|
async def test_deprecation_warning(hass, caplog):
|
||||||
"""Test deprecation warning."""
|
"""Test deprecation warning."""
|
||||||
hass.states.async_set("light.entity_off", "off", {})
|
hass.states.async_set("light.entity_off", "off", {})
|
||||||
|
@ -430,6 +430,8 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
"effect_list": YEELIGHT_MONO_EFFECT_LIST,
|
"effect_list": YEELIGHT_MONO_EFFECT_LIST,
|
||||||
"supported_features": SUPPORT_YEELIGHT,
|
"supported_features": SUPPORT_YEELIGHT,
|
||||||
"brightness": bright,
|
"brightness": bright,
|
||||||
|
"color_mode": "brightness",
|
||||||
|
"supported_color_modes": ["brightness"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -441,6 +443,8 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
"effect_list": YEELIGHT_MONO_EFFECT_LIST,
|
"effect_list": YEELIGHT_MONO_EFFECT_LIST,
|
||||||
"supported_features": SUPPORT_YEELIGHT,
|
"supported_features": SUPPORT_YEELIGHT,
|
||||||
"brightness": bright,
|
"brightness": bright,
|
||||||
|
"color_mode": "brightness",
|
||||||
|
"supported_color_modes": ["brightness"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -463,8 +467,14 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
"hs_color": hs_color,
|
"hs_color": hs_color,
|
||||||
"rgb_color": rgb_color,
|
"rgb_color": rgb_color,
|
||||||
"xy_color": xy_color,
|
"xy_color": xy_color,
|
||||||
|
"color_mode": "hs",
|
||||||
|
"supported_color_modes": ["color_temp", "hs"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"supported_features": 0,
|
||||||
|
"color_mode": "onoff",
|
||||||
|
"supported_color_modes": ["onoff"],
|
||||||
},
|
},
|
||||||
{"supported_features": 0},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# WhiteTemp
|
# WhiteTemp
|
||||||
@ -483,11 +493,15 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
),
|
),
|
||||||
"brightness": current_brightness,
|
"brightness": current_brightness,
|
||||||
"color_temp": ct,
|
"color_temp": ct,
|
||||||
|
"color_mode": "color_temp",
|
||||||
|
"supported_color_modes": ["color_temp"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
|
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
|
||||||
"supported_features": SUPPORT_YEELIGHT,
|
"supported_features": SUPPORT_YEELIGHT,
|
||||||
"brightness": nl_br,
|
"brightness": nl_br,
|
||||||
|
"color_mode": "brightness",
|
||||||
|
"supported_color_modes": ["brightness"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -512,11 +526,15 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
),
|
),
|
||||||
"brightness": current_brightness,
|
"brightness": current_brightness,
|
||||||
"color_temp": ct,
|
"color_temp": ct,
|
||||||
|
"color_mode": "color_temp",
|
||||||
|
"supported_color_modes": ["color_temp"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
|
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
|
||||||
"supported_features": SUPPORT_YEELIGHT,
|
"supported_features": SUPPORT_YEELIGHT,
|
||||||
"brightness": nl_br,
|
"brightness": nl_br,
|
||||||
|
"color_mode": "brightness",
|
||||||
|
"supported_color_modes": ["brightness"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
await _async_test(
|
await _async_test(
|
||||||
@ -532,6 +550,8 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
"hs_color": bg_hs_color,
|
"hs_color": bg_hs_color,
|
||||||
"rgb_color": bg_rgb_color,
|
"rgb_color": bg_rgb_color,
|
||||||
"xy_color": bg_xy_color,
|
"xy_color": bg_xy_color,
|
||||||
|
"color_mode": "hs",
|
||||||
|
"supported_color_modes": ["color_temp", "hs"],
|
||||||
},
|
},
|
||||||
name=f"{UNIQUE_NAME} ambilight",
|
name=f"{UNIQUE_NAME} ambilight",
|
||||||
entity_id=f"{ENTITY_LIGHT}_ambilight",
|
entity_id=f"{ENTITY_LIGHT}_ambilight",
|
||||||
|
@ -7,9 +7,12 @@ import pyzerproc
|
|||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
|
ATTR_COLOR_MODE,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
ATTR_XY_COLOR,
|
ATTR_XY_COLOR,
|
||||||
|
COLOR_MODE_HS,
|
||||||
SCAN_INTERVAL,
|
SCAN_INTERVAL,
|
||||||
SUPPORT_BRIGHTNESS,
|
SUPPORT_BRIGHTNESS,
|
||||||
SUPPORT_COLOR,
|
SUPPORT_COLOR,
|
||||||
@ -96,6 +99,7 @@ async def test_init(hass, mock_entry):
|
|||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
||||||
ATTR_ICON: "mdi:string-lights",
|
ATTR_ICON: "mdi:string-lights",
|
||||||
}
|
}
|
||||||
@ -104,8 +108,10 @@ async def test_init(hass, mock_entry):
|
|||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "LEDBlue-33445566",
|
ATTR_FRIENDLY_NAME: "LEDBlue-33445566",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
||||||
ATTR_ICON: "mdi:string-lights",
|
ATTR_ICON: "mdi:string-lights",
|
||||||
|
ATTR_COLOR_MODE: COLOR_MODE_HS,
|
||||||
ATTR_BRIGHTNESS: 255,
|
ATTR_BRIGHTNESS: 255,
|
||||||
ATTR_HS_COLOR: (221.176, 100.0),
|
ATTR_HS_COLOR: (221.176, 100.0),
|
||||||
ATTR_RGB_COLOR: (0, 80, 255),
|
ATTR_RGB_COLOR: (0, 80, 255),
|
||||||
@ -272,6 +278,7 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
||||||
ATTR_ICON: "mdi:string-lights",
|
ATTR_ICON: "mdi:string-lights",
|
||||||
}
|
}
|
||||||
@ -290,6 +297,7 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
||||||
ATTR_ICON: "mdi:string-lights",
|
ATTR_ICON: "mdi:string-lights",
|
||||||
}
|
}
|
||||||
@ -307,6 +315,7 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
||||||
ATTR_ICON: "mdi:string-lights",
|
ATTR_ICON: "mdi:string-lights",
|
||||||
}
|
}
|
||||||
@ -324,8 +333,10 @@ async def test_light_update(hass, mock_light):
|
|||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes == {
|
assert state.attributes == {
|
||||||
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
|
||||||
ATTR_ICON: "mdi:string-lights",
|
ATTR_ICON: "mdi:string-lights",
|
||||||
|
ATTR_COLOR_MODE: COLOR_MODE_HS,
|
||||||
ATTR_BRIGHTNESS: 220,
|
ATTR_BRIGHTNESS: 220,
|
||||||
ATTR_HS_COLOR: (261.429, 31.818),
|
ATTR_HS_COLOR: (261.429, 31.818),
|
||||||
ATTR_RGB_COLOR: (202, 173, 255),
|
ATTR_RGB_COLOR: (202, 173, 255),
|
||||||
|
@ -37,4 +37,17 @@ class MockLight(MockToggleEntity, LightEntity):
|
|||||||
"""Mock light class."""
|
"""Mock light class."""
|
||||||
|
|
||||||
brightness = None
|
brightness = None
|
||||||
|
supported_color_modes = None
|
||||||
supported_features = 0
|
supported_features = 0
|
||||||
|
|
||||||
|
color_mode = None
|
||||||
|
|
||||||
|
hs_color = None
|
||||||
|
xy_color = None
|
||||||
|
rgb_color = None
|
||||||
|
rgbw_color = None
|
||||||
|
rgbww_color = None
|
||||||
|
|
||||||
|
color_temp = None
|
||||||
|
|
||||||
|
white_value = None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user