Use supported color modes in deCONZ integration (#51656)

* Initial commit everything is working, need to reevaluate tests

* Fix supported color modes and hs_color

* Attest color mode
This commit is contained in:
Robert Svensson 2021-06-10 08:51:58 +02:00 committed by GitHub
parent 9097f41219
commit b1022ce84e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 93 additions and 36 deletions

View file

@ -1,5 +1,8 @@
"""Support for deCONZ lights."""
from __future__ import annotations
from pydeconz.group import DeconzGroup as Group
from pydeconz.light import Light
from homeassistant.components.light import (
@ -9,13 +12,16 @@ from homeassistant.components.light import (
ATTR_FLASH,
ATTR_HS_COLOR,
ATTR_TRANSITION,
ATTR_XY_COLOR,
COLOR_MODE_BRIGHTNESS,
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
COLOR_MODE_ONOFF,
COLOR_MODE_XY,
DOMAIN,
EFFECT_COLORLOOP,
FLASH_LONG,
FLASH_SHORT,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
@ -23,7 +29,6 @@ from homeassistant.components.light import (
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.util.color as color_util
from .const import (
COVER_TYPES,
@ -106,24 +111,50 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
"""Set up light."""
super().__init__(device, gateway)
self._attr_supported_color_modes = set()
self.update_features(self._device)
def update_features(self, device):
def update_features(self, device: Light | Group) -> None:
"""Calculate supported features of device."""
supported_color_modes = self._attr_supported_color_modes
if device.ct is not None:
supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
if device.hue is not None and device.sat is not None:
supported_color_modes.add(COLOR_MODE_HS)
if device.xy is not None:
supported_color_modes.add(COLOR_MODE_XY)
if not supported_color_modes and device.brightness is not None:
supported_color_modes.add(COLOR_MODE_BRIGHTNESS)
if not supported_color_modes:
supported_color_modes.add(COLOR_MODE_ONOFF)
if device.brightness is not None:
self._attr_supported_features |= SUPPORT_BRIGHTNESS
self._attr_supported_features |= SUPPORT_FLASH
self._attr_supported_features |= SUPPORT_TRANSITION
if device.ct is not None:
self._attr_supported_features |= SUPPORT_COLOR_TEMP
if device.xy is not None or (device.hue is not None and device.sat is not None):
self._attr_supported_features |= SUPPORT_COLOR
if device.effect is not None:
self._attr_supported_features |= SUPPORT_EFFECT
@property
def color_mode(self) -> str:
"""Return the color mode of the light."""
if self._device.colormode == "ct":
color_mode = COLOR_MODE_COLOR_TEMP
elif self._device.colormode == "hs":
color_mode = COLOR_MODE_HS
elif self._device.colormode == "xy":
color_mode = COLOR_MODE_XY
elif self._device.brightness is not None:
color_mode = COLOR_MODE_BRIGHTNESS
else:
color_mode = COLOR_MODE_ONOFF
return color_mode
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
@ -137,20 +168,17 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
@property
def color_temp(self):
"""Return the CT color value."""
if self._device.colormode != "ct":
return None
return self._device.ct
@property
def hs_color(self):
def hs_color(self) -> tuple:
"""Return the hs color value."""
if self._device.colormode in ("xy", "hs"):
if self._device.xy:
return color_util.color_xy_to_hs(*self._device.xy)
if self._device.hue and self._device.sat:
return (self._device.hue / 65535 * 360, self._device.sat / 255 * 100)
return None
@property
def xy_color(self) -> tuple | None:
"""Return the XY color value."""
return self._device.xy
@property
def is_on(self):
@ -161,18 +189,18 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
"""Turn on light."""
data = {"on": True}
if ATTR_BRIGHTNESS in kwargs:
data["bri"] = kwargs[ATTR_BRIGHTNESS]
if ATTR_COLOR_TEMP in kwargs:
data["ct"] = kwargs[ATTR_COLOR_TEMP]
if ATTR_HS_COLOR in kwargs:
if self._device.xy is not None:
data["xy"] = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR])
else:
data["hue"] = int(kwargs[ATTR_HS_COLOR][0] / 360 * 65535)
data["sat"] = int(kwargs[ATTR_HS_COLOR][1] / 100 * 255)
if ATTR_BRIGHTNESS in kwargs:
data["bri"] = kwargs[ATTR_BRIGHTNESS]
if ATTR_XY_COLOR in kwargs:
data["xy"] = kwargs[ATTR_XY_COLOR]
if ATTR_TRANSITION in kwargs:
data["transitiontime"] = int(kwargs[ATTR_TRANSITION] * 10)
@ -250,6 +278,21 @@ class DeconzGroup(DeconzBaseLight):
if light.ZHATYPE == Light.ZHATYPE:
self.update_features(light)
for exclusive_color_mode in [COLOR_MODE_ONOFF, COLOR_MODE_BRIGHTNESS]:
if (
exclusive_color_mode in self._attr_supported_color_modes
and len(self._attr_supported_color_modes) > 1
):
self._attr_supported_color_modes.remove(exclusive_color_mode)
@property
def hs_color(self) -> tuple | None:
"""Return the hs color value."""
try:
return super().hs_color
except TypeError:
return None
@property
def unique_id(self):
"""Return a unique identifier for this device."""

View file

@ -7,13 +7,19 @@ import pytest
from homeassistant.components.deconz.const import CONF_ALLOW_DECONZ_GROUPS
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_COLOR_TEMP,
ATTR_EFFECT,
ATTR_FLASH,
ATTR_HS_COLOR,
ATTR_MAX_MIREDS,
ATTR_MIN_MIREDS,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION,
ATTR_XY_COLOR,
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_ONOFF,
COLOR_MODE_XY,
DOMAIN as LIGHT_DOMAIN,
EFFECT_COLORLOOP,
FLASH_LONG,
@ -73,7 +79,7 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
"bri": 255,
"colormode": "xy",
"effect": "colorloop",
"xy": (500, 500),
"xy": (0.5, 0.5),
"reachable": True,
},
"type": "Extended color light",
@ -117,16 +123,22 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
rgb_light = hass.states.get("light.rgb_light")
assert rgb_light.state == STATE_ON
assert rgb_light.attributes[ATTR_BRIGHTNESS] == 255
assert rgb_light.attributes[ATTR_HS_COLOR] == (224.235, 100.0)
assert rgb_light.attributes[ATTR_XY_COLOR] == (0.5, 0.5)
assert rgb_light.attributes[ATTR_SUPPORTED_COLOR_MODES] == [COLOR_MODE_XY]
assert rgb_light.attributes[ATTR_COLOR_MODE] == COLOR_MODE_XY
assert rgb_light.attributes[ATTR_SUPPORTED_FEATURES] == 44
assert rgb_light.attributes["is_deconz_group"] is False
assert rgb_light.attributes[ATTR_SUPPORTED_FEATURES] == 61
tunable_white_light = hass.states.get("light.tunable_white_light")
assert tunable_white_light.state == STATE_ON
assert tunable_white_light.attributes[ATTR_COLOR_TEMP] == 2500
assert tunable_white_light.attributes[ATTR_MAX_MIREDS] == 454
assert tunable_white_light.attributes[ATTR_MIN_MIREDS] == 155
assert tunable_white_light.attributes[ATTR_SUPPORTED_FEATURES] == 2
assert tunable_white_light.attributes[ATTR_SUPPORTED_COLOR_MODES] == [
COLOR_MODE_COLOR_TEMP
]
assert tunable_white_light.attributes[ATTR_COLOR_MODE] == COLOR_MODE_COLOR_TEMP
assert tunable_white_light.attributes[ATTR_SUPPORTED_FEATURES] == 0
tunable_white_light_bad_maxmin = hass.states.get(
"light.tunable_white_light_with_bad_maxmin_values"
@ -135,10 +147,12 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
assert tunable_white_light_bad_maxmin.attributes[ATTR_COLOR_TEMP] == 2500
assert tunable_white_light_bad_maxmin.attributes[ATTR_MAX_MIREDS] == 650
assert tunable_white_light_bad_maxmin.attributes[ATTR_MIN_MIREDS] == 140
assert tunable_white_light_bad_maxmin.attributes[ATTR_SUPPORTED_FEATURES] == 2
assert tunable_white_light_bad_maxmin.attributes[ATTR_SUPPORTED_FEATURES] == 0
on_off_light = hass.states.get("light.on_off_light")
assert on_off_light.state == STATE_ON
assert on_off_light.attributes[ATTR_SUPPORTED_COLOR_MODES] == [COLOR_MODE_ONOFF]
assert on_off_light.attributes[ATTR_COLOR_MODE] == COLOR_MODE_ONOFF
assert on_off_light.attributes[ATTR_SUPPORTED_FEATURES] == 0
assert hass.states.get("light.light_group").state == STATE_ON
@ -191,7 +205,7 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: "light.rgb_light",
ATTR_HS_COLOR: (20, 30),
ATTR_XY_COLOR: (0.411, 0.351),
ATTR_FLASH: FLASH_LONG,
ATTR_EFFECT: "None",
},
@ -598,4 +612,4 @@ async def test_verify_group_supported_features(hass, aioclient_mock):
assert len(hass.states.async_all()) == 4
assert hass.states.get("light.group").state == STATE_ON
assert hass.states.get("light.group").attributes[ATTR_SUPPORTED_FEATURES] == 63
assert hass.states.get("light.group").attributes[ATTR_SUPPORTED_FEATURES] == 44