Update light turn_on schema to coerce colors to tuple before asserting sequence type (#58670)

* Make color_name_to_rgb return a tuple

* Tweak

* Tweak

* Update test

* Tweak test
pull/58977/head
Erik Montnemery 2021-10-29 15:51:14 +02:00 committed by Paulus Schoutsen
parent 78082afa94
commit e9b67b3590
4 changed files with 273 additions and 9 deletions

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from collections import Counter
import itertools
import logging
from typing import Any, Set, cast
import voluptuous as vol
@ -66,6 +67,8 @@ SUPPORT_GROUP_LIGHT = (
SUPPORT_EFFECT | SUPPORT_FLASH | SUPPORT_TRANSITION | SUPPORT_WHITE_VALUE
)
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(
hass: HomeAssistant,
@ -152,6 +155,8 @@ class LightGroup(GroupEntity, light.LightEntity):
}
data[ATTR_ENTITY_ID] = self._entity_ids
_LOGGER.debug("Forwarded turn_on command: %s", data)
await self.hass.services.async_call(
light.DOMAIN,
light.SERVICE_TURN_ON,

View File

@ -202,25 +202,25 @@ LIGHT_TURN_ON_SCHEMA = {
),
vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): cv.positive_int,
vol.Exclusive(ATTR_HS_COLOR, COLOR_GROUP): vol.All(
vol.Coerce(tuple),
vol.ExactSequence(
(
vol.All(vol.Coerce(float), vol.Range(min=0, max=360)),
vol.All(vol.Coerce(float), vol.Range(min=0, max=100)),
)
),
vol.Coerce(tuple),
),
vol.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.byte,) * 3), vol.Coerce(tuple)
vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3)
),
vol.Exclusive(ATTR_RGBW_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.byte,) * 4), vol.Coerce(tuple)
vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 4)
),
vol.Exclusive(ATTR_RGBWW_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.byte,) * 5), vol.Coerce(tuple)
vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 5)
),
vol.Exclusive(ATTR_XY_COLOR, COLOR_GROUP): vol.All(
vol.ExactSequence((cv.small_float, cv.small_float)), vol.Coerce(tuple)
vol.Coerce(tuple), vol.ExactSequence((cv.small_float, cv.small_float))
),
vol.Exclusive(ATTR_WHITE, COLOR_GROUP): VALID_BRIGHTNESS,
ATTR_WHITE_VALUE: vol.All(vol.Coerce(int), vol.Range(min=0, max=255)),

View File

@ -3,12 +3,15 @@ from os import path
import unittest.mock
from unittest.mock import MagicMock, patch
import pytest
from homeassistant import config as hass_config
from homeassistant.components.group import DOMAIN, SERVICE_RELOAD
import homeassistant.components.group.light as group
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_COLOR_NAME,
ATTR_COLOR_TEMP,
ATTR_EFFECT,
ATTR_EFFECT_LIST,
@ -28,6 +31,7 @@ from homeassistant.components.light import (
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
COLOR_MODE_ONOFF,
COLOR_MODE_RGB,
COLOR_MODE_RGBW,
COLOR_MODE_RGBWW,
COLOR_MODE_WHITE,
@ -261,6 +265,77 @@ async def test_color_hs(hass, enable_custom_integrations):
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
async def test_color_rgb(hass, enable_custom_integrations):
"""Test rgbw color reporting."""
platform = getattr(hass.components, "test.light")
platform.init(empty=True)
platform.ENTITIES.append(platform.MockLight("test1", STATE_ON))
platform.ENTITIES.append(platform.MockLight("test2", STATE_OFF))
entity0 = platform.ENTITIES[0]
entity0.supported_color_modes = {COLOR_MODE_RGB}
entity0.color_mode = COLOR_MODE_RGB
entity0.brightness = 255
entity0.rgb_color = (0, 64, 128)
entity1 = platform.ENTITIES[1]
entity1.supported_color_modes = {COLOR_MODE_RGB}
entity1.color_mode = COLOR_MODE_RGB
entity1.brightness = 255
entity1.rgb_color = (255, 128, 64)
assert await async_setup_component(
hass,
LIGHT_DOMAIN,
{
LIGHT_DOMAIN: [
{"platform": "test"},
{
"platform": DOMAIN,
"entities": ["light.test1", "light.test2"],
},
]
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
state = hass.states.get("light.light_group")
assert state.state == STATE_ON
assert state.attributes[ATTR_COLOR_MODE] == "rgb"
assert state.attributes[ATTR_RGB_COLOR] == (0, 64, 128)
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
await hass.services.async_call(
"light",
"turn_on",
{"entity_id": [entity1.entity_id]},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("light.light_group")
assert state.attributes[ATTR_COLOR_MODE] == "rgb"
assert state.attributes[ATTR_RGB_COLOR] == (127, 96, 96)
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
await hass.services.async_call(
"light",
"turn_off",
{"entity_id": [entity0.entity_id]},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("light.light_group")
assert state.attributes[ATTR_COLOR_MODE] == "rgb"
assert state.attributes[ATTR_RGB_COLOR] == (255, 128, 64)
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
async def test_color_rgbw(hass, enable_custom_integrations):
"""Test rgbw color reporting."""
platform = getattr(hass.components, "test.light")
@ -1039,14 +1114,40 @@ async def test_supported_features(hass):
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 40
async def test_service_calls(hass):
@pytest.mark.parametrize("supported_color_modes", [COLOR_MODE_HS, COLOR_MODE_RGB])
async def test_service_calls(hass, enable_custom_integrations, supported_color_modes):
"""Test service calls."""
platform = getattr(hass.components, "test.light")
platform.init(empty=True)
platform.ENTITIES.append(platform.MockLight("bed_light", STATE_ON))
platform.ENTITIES.append(platform.MockLight("ceiling_lights", STATE_OFF))
platform.ENTITIES.append(platform.MockLight("kitchen_lights", STATE_OFF))
entity0 = platform.ENTITIES[0]
entity0.supported_color_modes = {supported_color_modes}
entity0.color_mode = supported_color_modes
entity0.brightness = 255
entity0.rgb_color = (0, 64, 128)
entity1 = platform.ENTITIES[1]
entity1.supported_color_modes = {supported_color_modes}
entity1.color_mode = supported_color_modes
entity1.brightness = 255
entity1.rgb_color = (255, 128, 64)
entity2 = platform.ENTITIES[2]
entity2.supported_color_modes = {supported_color_modes}
entity2.color_mode = supported_color_modes
entity2.brightness = 255
entity2.rgb_color = (255, 128, 64)
await async_setup_component(
hass,
LIGHT_DOMAIN,
{
LIGHT_DOMAIN: [
{"platform": "demo"},
{"platform": "test"},
{
"platform": DOMAIN,
"entities": [
@ -1062,14 +1163,16 @@ async def test_service_calls(hass):
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("light.light_group").state == STATE_ON
group_state = hass.states.get("light.light_group")
assert group_state.state == STATE_ON
assert group_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [supported_color_modes]
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TOGGLE,
{ATTR_ENTITY_ID: "light.light_group"},
blocking=True,
)
assert hass.states.get("light.bed_light").state == STATE_OFF
assert hass.states.get("light.ceiling_lights").state == STATE_OFF
assert hass.states.get("light.kitchen_lights").state == STATE_OFF
@ -1096,6 +1199,84 @@ async def test_service_calls(hass):
assert hass.states.get("light.ceiling_lights").state == STATE_OFF
assert hass.states.get("light.kitchen_lights").state == STATE_OFF
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: "light.light_group",
ATTR_BRIGHTNESS: 128,
ATTR_RGB_COLOR: (42, 255, 255),
},
blocking=True,
)
state = hass.states.get("light.bed_light")
assert state.state == STATE_ON
assert state.attributes[ATTR_BRIGHTNESS] == 128
assert state.attributes[ATTR_RGB_COLOR] == (42, 255, 255)
state = hass.states.get("light.ceiling_lights")
assert state.state == STATE_ON
assert state.attributes[ATTR_BRIGHTNESS] == 128
assert state.attributes[ATTR_RGB_COLOR] == (42, 255, 255)
state = hass.states.get("light.kitchen_lights")
assert state.state == STATE_ON
assert state.attributes[ATTR_BRIGHTNESS] == 128
assert state.attributes[ATTR_RGB_COLOR] == (42, 255, 255)
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: "light.light_group",
ATTR_BRIGHTNESS: 128,
ATTR_COLOR_NAME: "red",
},
blocking=True,
)
state = hass.states.get("light.bed_light")
assert state.state == STATE_ON
assert state.attributes[ATTR_BRIGHTNESS] == 128
assert state.attributes[ATTR_RGB_COLOR] == (255, 0, 0)
state = hass.states.get("light.ceiling_lights")
assert state.state == STATE_ON
assert state.attributes[ATTR_BRIGHTNESS] == 128
assert state.attributes[ATTR_RGB_COLOR] == (255, 0, 0)
state = hass.states.get("light.kitchen_lights")
assert state.state == STATE_ON
assert state.attributes[ATTR_BRIGHTNESS] == 128
assert state.attributes[ATTR_RGB_COLOR] == (255, 0, 0)
async def test_service_call_effect(hass):
"""Test service calls."""
await async_setup_component(
hass,
LIGHT_DOMAIN,
{
LIGHT_DOMAIN: [
{"platform": "demo"},
{
"platform": DOMAIN,
"entities": [
"light.bed_light",
"light.ceiling_lights",
"light.kitchen_lights",
],
},
]
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("light.light_group").state == STATE_ON
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,

View File

@ -18,6 +18,7 @@ from homeassistant.const import (
)
from homeassistant.exceptions import Unauthorized
from homeassistant.setup import async_setup_component
import homeassistant.util.color as color_util
from tests.common import async_mock_service
@ -1589,6 +1590,83 @@ async def test_light_service_call_color_conversion(hass, enable_custom_integrati
assert data == {"brightness": 128, "rgbww_color": (0, 75, 140, 255, 255)}
async def test_light_service_call_color_conversion_named_tuple(
hass, enable_custom_integrations
):
"""Test a named tuple (RGBColor) is handled correctly."""
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))
platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON))
platform.ENTITIES.append(platform.MockLight("Test_rgbww", 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
entity5 = platform.ENTITIES[5]
entity5.supported_color_modes = {light.COLOR_MODE_RGBW}
entity6 = platform.ENTITIES[6]
entity6.supported_color_modes = {light.COLOR_MODE_RGBWW}
assert await async_setup_component(hass, "light", {"light": {"platform": "test"}})
await hass.async_block_till_done()
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,
entity5.entity_id,
entity6.entity_id,
],
"brightness_pct": 25,
"rgb_color": color_util.RGBColor(128, 0, 0),
},
blocking=True,
)
_, data = entity0.last_call("turn_on")
assert data == {"brightness": 64, "hs_color": (0.0, 100.0)}
_, data = entity1.last_call("turn_on")
assert data == {"brightness": 64, "rgb_color": (128, 0, 0)}
_, data = entity2.last_call("turn_on")
assert data == {"brightness": 64, "xy_color": (0.701, 0.299)}
_, data = entity3.last_call("turn_on")
assert data == {"brightness": 64, "rgb_color": (128, 0, 0)}
_, data = entity4.last_call("turn_on")
assert data == {"brightness": 64, "hs_color": (0.0, 100.0)}
_, data = entity5.last_call("turn_on")
assert data == {"brightness": 64, "rgbw_color": (128, 0, 0, 0)}
_, data = entity6.last_call("turn_on")
assert data == {"brightness": 64, "rgbww_color": (128, 0, 0, 0, 0)}
async def test_light_service_call_color_temp_emulation(
hass, enable_custom_integrations
):