mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Support KNX lights with multiple color modes (#130842)
This commit is contained in:
parent
c154ac26eb
commit
2f1c1d66cb
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, cast
|
||||
|
||||
from propcache import cached_property
|
||||
from xknx import XKNX
|
||||
from xknx.devices.light import ColorTemperatureType, Light as XknxLight, XYYColor
|
||||
|
||||
@ -389,39 +390,47 @@ class _KnxLight(LightEntity):
|
||||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def color_mode(self) -> ColorMode:
|
||||
"""Return the color mode of the light."""
|
||||
if self._device.supports_xyy_color:
|
||||
return ColorMode.XY
|
||||
if self._device.supports_hs_color:
|
||||
return ColorMode.HS
|
||||
if self._device.supports_rgbw:
|
||||
return ColorMode.RGBW
|
||||
if self._device.supports_color:
|
||||
return ColorMode.RGB
|
||||
@cached_property
|
||||
def supported_color_modes(self) -> set[ColorMode]:
|
||||
"""Get supported color modes."""
|
||||
color_mode = set()
|
||||
if (
|
||||
self._device.supports_color_temperature
|
||||
or self._device.supports_tunable_white
|
||||
):
|
||||
return ColorMode.COLOR_TEMP
|
||||
if self._device.supports_brightness:
|
||||
return ColorMode.BRIGHTNESS
|
||||
return ColorMode.ONOFF
|
||||
|
||||
@property
|
||||
def supported_color_modes(self) -> set[ColorMode]:
|
||||
"""Flag supported color modes."""
|
||||
return {self.color_mode}
|
||||
color_mode.add(ColorMode.COLOR_TEMP)
|
||||
if self._device.supports_xyy_color:
|
||||
color_mode.add(ColorMode.XY)
|
||||
if self._device.supports_rgbw:
|
||||
color_mode.add(ColorMode.RGBW)
|
||||
elif self._device.supports_color:
|
||||
# one of RGB or RGBW so individual color configurations work properly
|
||||
color_mode.add(ColorMode.RGB)
|
||||
if self._device.supports_hs_color:
|
||||
color_mode.add(ColorMode.HS)
|
||||
if not color_mode:
|
||||
# brightness or on/off must be the only supported mode
|
||||
if self._device.supports_brightness:
|
||||
color_mode.add(ColorMode.BRIGHTNESS)
|
||||
else:
|
||||
color_mode.add(ColorMode.ONOFF)
|
||||
return color_mode
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on."""
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||
color_temp = kwargs.get(ATTR_COLOR_TEMP_KELVIN)
|
||||
rgb = kwargs.get(ATTR_RGB_COLOR)
|
||||
rgbw = kwargs.get(ATTR_RGBW_COLOR)
|
||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||
xy_color = kwargs.get(ATTR_XY_COLOR)
|
||||
# LightEntity color translation will ensure that only attributes of supported
|
||||
# color modes are passed to this method - so we can't set unsupported mode here
|
||||
if color_temp := kwargs.get(ATTR_COLOR_TEMP_KELVIN):
|
||||
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||
if rgb := kwargs.get(ATTR_RGB_COLOR):
|
||||
self._attr_color_mode = ColorMode.RGB
|
||||
if rgbw := kwargs.get(ATTR_RGBW_COLOR):
|
||||
self._attr_color_mode = ColorMode.RGBW
|
||||
if hs_color := kwargs.get(ATTR_HS_COLOR):
|
||||
self._attr_color_mode = ColorMode.HS
|
||||
if xy_color := kwargs.get(ATTR_XY_COLOR):
|
||||
self._attr_color_mode = ColorMode.XY
|
||||
|
||||
if (
|
||||
not self.is_on
|
||||
@ -500,17 +509,17 @@ class _KnxLight(LightEntity):
|
||||
await self._device.set_brightness(brightness)
|
||||
return
|
||||
# brightness without color in kwargs; set via color
|
||||
if self.color_mode == ColorMode.XY:
|
||||
if self._attr_color_mode == ColorMode.XY:
|
||||
await self._device.set_xyy_color(XYYColor(brightness=brightness))
|
||||
return
|
||||
# default to white if color not known for RGB(W)
|
||||
if self.color_mode == ColorMode.RGBW:
|
||||
if self._attr_color_mode == ColorMode.RGBW:
|
||||
_rgbw = self.rgbw_color
|
||||
if not _rgbw or not any(_rgbw):
|
||||
_rgbw = (0, 0, 0, 255)
|
||||
await set_color(_rgbw[:3], _rgbw[3], brightness)
|
||||
return
|
||||
if self.color_mode == ColorMode.RGB:
|
||||
if self._attr_color_mode == ColorMode.RGB:
|
||||
_rgb = self.rgb_color
|
||||
if not _rgb or not any(_rgb):
|
||||
_rgb = (255, 255, 255)
|
||||
@ -533,6 +542,7 @@ class KnxYamlLight(_KnxLight, KnxYamlEntity):
|
||||
knx_module=knx_module,
|
||||
device=_create_yaml_light(knx_module.xknx, config),
|
||||
)
|
||||
self._attr_color_mode = next(iter(self.supported_color_modes))
|
||||
self._attr_max_color_temp_kelvin: int = config[LightSchema.CONF_MAX_KELVIN]
|
||||
self._attr_min_color_temp_kelvin: int = config[LightSchema.CONF_MIN_KELVIN]
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
@ -566,5 +576,6 @@ class KnxUiLight(_KnxLight, KnxUiEntity):
|
||||
self._device = _create_ui_light(
|
||||
knx_module.xknx, config[DOMAIN], config[CONF_ENTITY][CONF_NAME]
|
||||
)
|
||||
self._attr_color_mode = next(iter(self.supported_color_modes))
|
||||
self._attr_max_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MAX]
|
||||
self._attr_min_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MIN]
|
||||
|
@ -41,7 +41,11 @@ async def test_light_simple(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
}
|
||||
)
|
||||
|
||||
knx.assert_state("light.test", STATE_OFF)
|
||||
knx.assert_state(
|
||||
"light.test",
|
||||
STATE_OFF,
|
||||
supported_color_modes=[ColorMode.ONOFF],
|
||||
)
|
||||
# turn on light
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
@ -110,6 +114,7 @@ async def test_light_brightness(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=80,
|
||||
supported_color_modes=[ColorMode.BRIGHTNESS],
|
||||
color_mode=ColorMode.BRIGHTNESS,
|
||||
)
|
||||
# receive brightness changes from KNX
|
||||
@ -165,6 +170,7 @@ async def test_light_color_temp_absolute(hass: HomeAssistant, knx: KNXTestKit) -
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.COLOR_TEMP],
|
||||
color_mode=ColorMode.COLOR_TEMP,
|
||||
color_temp=370,
|
||||
color_temp_kelvin=2700,
|
||||
@ -227,6 +233,7 @@ async def test_light_color_temp_relative(hass: HomeAssistant, knx: KNXTestKit) -
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.COLOR_TEMP],
|
||||
color_mode=ColorMode.COLOR_TEMP,
|
||||
color_temp=250,
|
||||
color_temp_kelvin=4000,
|
||||
@ -300,6 +307,7 @@ async def test_light_hs_color(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.HS],
|
||||
color_mode=ColorMode.HS,
|
||||
hs_color=(360, 100),
|
||||
)
|
||||
@ -375,6 +383,7 @@ async def test_light_xyy_color(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=204,
|
||||
supported_color_modes=[ColorMode.XY],
|
||||
color_mode=ColorMode.XY,
|
||||
xy_color=(0.8, 0.8),
|
||||
)
|
||||
@ -457,6 +466,7 @@ async def test_light_xyy_color_with_brightness(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255, # brightness form xyy_color ignored when extra brightness GA is used
|
||||
supported_color_modes=[ColorMode.XY],
|
||||
color_mode=ColorMode.XY,
|
||||
xy_color=(0.8, 0.8),
|
||||
)
|
||||
@ -543,6 +553,7 @@ async def test_light_rgb_individual(hass: HomeAssistant, knx: KNXTestKit) -> Non
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.RGB],
|
||||
color_mode=ColorMode.RGB,
|
||||
rgb_color=(255, 255, 255),
|
||||
)
|
||||
@ -699,6 +710,7 @@ async def test_light_rgbw_individual(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.RGBW],
|
||||
color_mode=ColorMode.RGBW,
|
||||
rgbw_color=(0, 0, 0, 255),
|
||||
)
|
||||
@ -853,6 +865,7 @@ async def test_light_rgb(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.RGB],
|
||||
color_mode=ColorMode.RGB,
|
||||
rgb_color=(255, 255, 255),
|
||||
)
|
||||
@ -961,6 +974,7 @@ async def test_light_rgbw(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.RGBW],
|
||||
color_mode=ColorMode.RGBW,
|
||||
rgbw_color=(255, 101, 102, 103),
|
||||
)
|
||||
@ -1078,6 +1092,7 @@ async def test_light_rgbw_brightness(hass: HomeAssistant, knx: KNXTestKit) -> No
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
supported_color_modes=[ColorMode.RGBW],
|
||||
color_mode=ColorMode.RGBW,
|
||||
rgbw_color=(255, 101, 102, 103),
|
||||
)
|
||||
@ -1174,8 +1189,12 @@ async def test_light_ui_create(
|
||||
# created entity sends read-request to KNX bus
|
||||
await knx.assert_read("2/2/2")
|
||||
await knx.receive_response("2/2/2", True)
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state is STATE_ON
|
||||
knx.assert_state(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
supported_color_modes=[ColorMode.ONOFF],
|
||||
color_mode=ColorMode.ONOFF,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -1216,9 +1235,103 @@ async def test_light_ui_color_temp(
|
||||
blocking=True,
|
||||
)
|
||||
await knx.assert_write("3/3/3", raw_ct)
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state is STATE_ON
|
||||
assert state.attributes[ATTR_COLOR_TEMP_KELVIN] == pytest.approx(4200, abs=1)
|
||||
knx.assert_state(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
supported_color_modes=[ColorMode.COLOR_TEMP],
|
||||
color_mode=ColorMode.COLOR_TEMP,
|
||||
color_temp_kelvin=pytest.approx(4200, abs=1),
|
||||
)
|
||||
|
||||
|
||||
async def test_light_ui_multi_mode(
|
||||
hass: HomeAssistant,
|
||||
knx: KNXTestKit,
|
||||
create_ui_entity: KnxEntityGenerator,
|
||||
) -> None:
|
||||
"""Test creating a light with multiple color modes."""
|
||||
await knx.setup_integration({})
|
||||
await create_ui_entity(
|
||||
platform=Platform.LIGHT,
|
||||
entity_data={"name": "test"},
|
||||
knx_data={
|
||||
"color_temp_min": 2700,
|
||||
"color_temp_max": 6000,
|
||||
"_light_color_mode_schema": "default",
|
||||
"ga_switch": {
|
||||
"write": "1/1/1",
|
||||
"passive": [],
|
||||
"state": "2/2/2",
|
||||
},
|
||||
"sync_state": True,
|
||||
"ga_brightness": {
|
||||
"write": "0/6/0",
|
||||
"state": "0/6/1",
|
||||
"passive": [],
|
||||
},
|
||||
"ga_color_temp": {
|
||||
"write": "0/6/2",
|
||||
"dpt": "7.600",
|
||||
"state": "0/6/3",
|
||||
"passive": [],
|
||||
},
|
||||
"ga_color": {
|
||||
"write": "0/6/4",
|
||||
"dpt": "251.600",
|
||||
"state": "0/6/5",
|
||||
"passive": [],
|
||||
},
|
||||
},
|
||||
)
|
||||
await knx.assert_read("2/2/2", True)
|
||||
await knx.assert_read("0/6/1", (0xFF,))
|
||||
await knx.assert_read("0/6/5", (0xFF, 0x65, 0x66, 0x67, 0x00, 0x0F))
|
||||
await knx.assert_read("0/6/3", (0x12, 0x34))
|
||||
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_on",
|
||||
{
|
||||
"entity_id": "light.test",
|
||||
ATTR_COLOR_NAME: "hotpink",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await knx.assert_write("0/6/4", (255, 0, 128, 178, 0, 15))
|
||||
knx.assert_state(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
color_temp_kelvin=None,
|
||||
rgbw_color=(255, 0, 128, 178),
|
||||
supported_color_modes=[
|
||||
ColorMode.COLOR_TEMP,
|
||||
ColorMode.RGBW,
|
||||
],
|
||||
color_mode=ColorMode.RGBW,
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_on",
|
||||
{
|
||||
"entity_id": "light.test",
|
||||
ATTR_COLOR_TEMP_KELVIN: 4200,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await knx.assert_write("0/6/2", (0x10, 0x68))
|
||||
knx.assert_state(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
brightness=255,
|
||||
color_temp_kelvin=4200,
|
||||
rgbw_color=None,
|
||||
supported_color_modes=[
|
||||
ColorMode.COLOR_TEMP,
|
||||
ColorMode.RGBW,
|
||||
],
|
||||
color_mode=ColorMode.COLOR_TEMP,
|
||||
)
|
||||
|
||||
|
||||
async def test_light_ui_load(
|
||||
@ -1234,8 +1347,12 @@ async def test_light_ui_load(
|
||||
# unrelated switch in config store
|
||||
await knx.assert_read("1/0/45", response=True, ignore_order=True)
|
||||
|
||||
state = hass.states.get("light.test")
|
||||
assert state.state is STATE_ON
|
||||
knx.assert_state(
|
||||
"light.test",
|
||||
STATE_ON,
|
||||
supported_color_modes=[ColorMode.ONOFF],
|
||||
color_mode=ColorMode.ONOFF,
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get("light.test")
|
||||
assert entity.entity_category is EntityCategory.CONFIG
|
||||
|
Loading…
x
Reference in New Issue
Block a user