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:
Erik Montnemery 2021-03-16 12:51:39 +01:00 committed by GitHub
parent 333e6a215a
commit 5f2326fb57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 897 additions and 39 deletions

View File

@ -6,7 +6,7 @@ import dataclasses
from datetime import timedelta
import logging
import os
from typing import Dict, List, Optional, Tuple, cast
from typing import Dict, List, Optional, Set, Tuple, cast
import voluptuous as vol
@ -38,19 +38,49 @@ DATA_PROFILES = "light_profiles"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
# Bitfield of features supported by the light entity
SUPPORT_BRIGHTNESS = 1
SUPPORT_COLOR_TEMP = 2
SUPPORT_BRIGHTNESS = 1 # Deprecated, replaced by color modes
SUPPORT_COLOR_TEMP = 2 # Deprecated, replaced by color modes
SUPPORT_EFFECT = 4
SUPPORT_FLASH = 8
SUPPORT_COLOR = 16
SUPPORT_COLOR = 16 # Deprecated, replaced by color modes
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.
ATTR_TRANSITION = "transition"
# Lists holding color values
ATTR_RGB_COLOR = "rgb_color"
ATTR_RGBW_COLOR = "rgbw_color"
ATTR_RGBWW_COLOR = "rgbww_color"
ATTR_XY_COLOR = "xy_color"
ATTR_HS_COLOR = "hs_color"
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_COLOR_NAME, COLOR_GROUP): cv.string,
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.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:
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):
"""Filter out params not used in turn off."""
@ -228,6 +256,52 @@ async def async_setup(hass, config):
if ATTR_PROFILE not in 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
if params.get(ATTR_BRIGHTNESS) == 0:
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 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
def hs_color(self) -> Optional[Tuple[float, float]]:
"""Return the hue and saturation color value [float, float]."""
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
def color_temp(self) -> Optional[int]:
"""Return the CT color value in mireds."""
@ -463,6 +609,29 @@ class LightEntity(ToggleEntity):
if supported_features & SUPPORT_EFFECT:
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
@property
@ -473,27 +642,85 @@ class LightEntity(ToggleEntity):
data = {}
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
if supported_features & SUPPORT_COLOR_TEMP:
if color_mode == COLOR_MODE_COLOR_TEMP:
data[ATTR_COLOR_TEMP] = self.color_temp
if supported_features & SUPPORT_COLOR 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)
if color_mode in COLOR_MODES_COLOR:
data.update(self._light_internal_convert_color(color_mode))
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
if self.hs_color is not None:
data.update(self._light_internal_convert_color(COLOR_MODE_HS))
if supported_features & SUPPORT_EFFECT:
data[ATTR_EFFECT] = self.effect
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
def supported_features(self) -> int:
"""Flag supported features."""

View File

@ -17,6 +17,7 @@ from homeassistant.helpers.typing import HomeAssistantType
from . import (
ATTR_BRIGHTNESS,
ATTR_BRIGHTNESS_PCT,
ATTR_COLOR_MODE,
ATTR_COLOR_NAME,
ATTR_COLOR_TEMP,
ATTR_EFFECT,
@ -25,9 +26,18 @@ from . import (
ATTR_KELVIN,
ATTR_PROFILE,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_RGBWW_COLOR,
ATTR_TRANSITION,
ATTR_WHITE_VALUE,
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,
)
@ -48,6 +58,8 @@ COLOR_GROUP = [
ATTR_HS_COLOR,
ATTR_COLOR_TEMP,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_RGBWW_COLOR,
ATTR_XY_COLOR,
# The following color attributes are deprecated
ATTR_PROFILE,
@ -55,6 +67,15 @@ COLOR_GROUP = [
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 = [
ATTR_BRIGHTNESS_PCT,
ATTR_COLOR_NAME,
@ -114,11 +135,29 @@ async def _async_reproduce_state(
if attr in state.attributes:
service_data[attr] = state.attributes[attr]
for color_attr in COLOR_GROUP:
# Choose the first color that is specified
if color_attr in state.attributes:
if (
state.attributes.get(ATTR_COLOR_MODE, COLOR_MODE_UNKNOWN)
!= 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]
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:
service = SERVICE_TURN_OFF

View File

@ -8,10 +8,15 @@ from homeassistant import setup
from homeassistant.components.kulersky.light import DOMAIN
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_WHITE_VALUE,
ATTR_XY_COLOR,
COLOR_MODE_HS,
COLOR_MODE_RGBW,
SCAN_INTERVAL,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
@ -65,6 +70,7 @@ async def test_init(hass, mock_light):
assert state.state == STATE_OFF
assert state.attributes == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
| SUPPORT_COLOR
| SUPPORT_WHITE_VALUE,
@ -168,6 +174,7 @@ async def test_light_update(hass, mock_light):
assert state.state == STATE_OFF
assert state.attributes == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
| SUPPORT_COLOR
| SUPPORT_WHITE_VALUE,
@ -183,6 +190,7 @@ async def test_light_update(hass, mock_light):
assert state.state == STATE_UNAVAILABLE
assert state.attributes == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
| SUPPORT_COLOR
| SUPPORT_WHITE_VALUE,
@ -198,12 +206,15 @@ async def test_light_update(hass, mock_light):
assert state.state == STATE_ON
assert state.attributes == {
ATTR_FRIENDLY_NAME: "Bedroom",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS, COLOR_MODE_RGBW],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS
| SUPPORT_COLOR
| SUPPORT_WHITE_VALUE,
ATTR_COLOR_MODE: COLOR_MODE_RGBW,
ATTR_BRIGHTNESS: 200,
ATTR_HS_COLOR: (200, 60),
ATTR_RGB_COLOR: (102, 203, 255),
ATTR_RGBW_COLOR: (102, 203, 255, 240),
ATTR_WHITE_VALUE: 240,
ATTR_XY_COLOR: (0.184, 0.261),
}

View File

@ -915,3 +915,482 @@ invalid_no_brightness_no_color_no_transition,,,
"invalid_no_brightness_no_color_no_transition",
):
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)

View File

@ -1,4 +1,7 @@
"""Test reproduce state for Light."""
import pytest
from homeassistant.components import light
from homeassistant.components.light.reproduce_state import DEPRECATION_WARNING
from homeassistant.core import State
@ -15,6 +18,8 @@ VALID_HS_COLOR = {"hs_color": (345, 75)}
VALID_KELVIN = {"kelvin": 4000}
VALID_PROFILE = {"profile": "relax"}
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)}
@ -91,51 +96,51 @@ async def test_reproducing_states(hass, caplog):
expected_calls = []
expected_off = VALID_BRIGHTNESS
expected_off = dict(VALID_BRIGHTNESS)
expected_off["entity_id"] = "light.entity_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_calls.append(expected_bright)
expected_white = VALID_FLASH
expected_white = dict(VALID_FLASH)
expected_white["entity_id"] = "light.entity_white"
expected_calls.append(expected_white)
expected_flash = VALID_EFFECT
expected_flash = dict(VALID_EFFECT)
expected_flash["entity_id"] = "light.entity_flash"
expected_calls.append(expected_flash)
expected_effect = VALID_TRANSITION
expected_effect = dict(VALID_TRANSITION)
expected_effect["entity_id"] = "light.entity_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_calls.append(expected_trans)
expected_name = VALID_COLOR_TEMP
expected_name = dict(VALID_COLOR_TEMP)
expected_name["entity_id"] = "light.entity_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_calls.append(expected_temp)
expected_hs = VALID_KELVIN
expected_hs = dict(VALID_KELVIN)
expected_hs["entity_id"] = "light.entity_hs"
expected_calls.append(expected_hs)
expected_kelvin = VALID_PROFILE
expected_kelvin = dict(VALID_PROFILE)
expected_kelvin["entity_id"] = "light.entity_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_calls.append(expected_profile)
expected_rgb = VALID_XY_COLOR
expected_rgb = dict(VALID_XY_COLOR)
expected_rgb["entity_id"] = "light.entity_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"}
@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):
"""Test deprecation warning."""
hass.states.async_set("light.entity_off", "off", {})

View File

@ -430,6 +430,8 @@ async def test_device_types(hass: HomeAssistant):
"effect_list": YEELIGHT_MONO_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT,
"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,
"supported_features": SUPPORT_YEELIGHT,
"brightness": bright,
"color_mode": "brightness",
"supported_color_modes": ["brightness"],
},
)
@ -463,8 +467,14 @@ async def test_device_types(hass: HomeAssistant):
"hs_color": hs_color,
"rgb_color": rgb_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
@ -483,11 +493,15 @@ async def test_device_types(hass: HomeAssistant):
),
"brightness": current_brightness,
"color_temp": ct,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp"],
},
{
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT,
"brightness": nl_br,
"color_mode": "brightness",
"supported_color_modes": ["brightness"],
},
)
@ -512,11 +526,15 @@ async def test_device_types(hass: HomeAssistant):
),
"brightness": current_brightness,
"color_temp": ct,
"color_mode": "color_temp",
"supported_color_modes": ["color_temp"],
},
{
"effect_list": YEELIGHT_TEMP_ONLY_EFFECT_LIST,
"supported_features": SUPPORT_YEELIGHT,
"brightness": nl_br,
"color_mode": "brightness",
"supported_color_modes": ["brightness"],
},
)
await _async_test(
@ -532,6 +550,8 @@ async def test_device_types(hass: HomeAssistant):
"hs_color": bg_hs_color,
"rgb_color": bg_rgb_color,
"xy_color": bg_xy_color,
"color_mode": "hs",
"supported_color_modes": ["color_temp", "hs"],
},
name=f"{UNIQUE_NAME} ambilight",
entity_id=f"{ENTITY_LIGHT}_ambilight",

View File

@ -7,9 +7,12 @@ import pyzerproc
from homeassistant import setup
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_XY_COLOR,
COLOR_MODE_HS,
SCAN_INTERVAL,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
@ -96,6 +99,7 @@ async def test_init(hass, mock_entry):
assert state.state == STATE_OFF
assert state.attributes == {
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
ATTR_ICON: "mdi:string-lights",
}
@ -104,8 +108,10 @@ async def test_init(hass, mock_entry):
assert state.state == STATE_ON
assert state.attributes == {
ATTR_FRIENDLY_NAME: "LEDBlue-33445566",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
ATTR_ICON: "mdi:string-lights",
ATTR_COLOR_MODE: COLOR_MODE_HS,
ATTR_BRIGHTNESS: 255,
ATTR_HS_COLOR: (221.176, 100.0),
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.attributes == {
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
ATTR_ICON: "mdi:string-lights",
}
@ -290,6 +297,7 @@ async def test_light_update(hass, mock_light):
assert state.state == STATE_UNAVAILABLE
assert state.attributes == {
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
ATTR_ICON: "mdi:string-lights",
}
@ -307,6 +315,7 @@ async def test_light_update(hass, mock_light):
assert state.state == STATE_OFF
assert state.attributes == {
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
ATTR_ICON: "mdi:string-lights",
}
@ -324,8 +333,10 @@ async def test_light_update(hass, mock_light):
assert state.state == STATE_ON
assert state.attributes == {
ATTR_FRIENDLY_NAME: "LEDBlue-CCDDEEFF",
ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS],
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS | SUPPORT_COLOR,
ATTR_ICON: "mdi:string-lights",
ATTR_COLOR_MODE: COLOR_MODE_HS,
ATTR_BRIGHTNESS: 220,
ATTR_HS_COLOR: (261.429, 31.818),
ATTR_RGB_COLOR: (202, 173, 255),

View File

@ -37,4 +37,17 @@ class MockLight(MockToggleEntity, LightEntity):
"""Mock light class."""
brightness = None
supported_color_modes = None
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