1
mirror of https://github.com/home-assistant/core synced 2024-09-03 08:14:07 +02:00

Migrate hue v1 light to color_mode (#69275)

* Migrate hue v1 light to color_mode

* Fix test

* Correct filter_supported_color_modes + add test

* Use ColorMode enum
This commit is contained in:
Erik Montnemery 2022-04-28 09:49:51 +02:00 committed by GitHub
parent 59c6282c6c
commit 573e966d74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 147 additions and 15 deletions

View File

@ -20,13 +20,12 @@ from homeassistant.components.light import (
EFFECT_RANDOM,
FLASH_LONG,
FLASH_SHORT,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
ColorMode,
LightEntity,
filter_supported_color_modes,
)
from homeassistant.core import callback
from homeassistant.exceptions import PlatformNotReady
@ -60,10 +59,24 @@ SCAN_INTERVAL = timedelta(seconds=5)
LOGGER = logging.getLogger(__name__)
COLOR_MODES_HUE_ON_OFF = {ColorMode.ONOFF}
COLOR_MODES_HUE_DIMMABLE = {ColorMode.BRIGHTNESS}
COLOR_MODES_HUE_COLOR_TEMP = {ColorMode.COLOR_TEMP}
COLOR_MODES_HUE_COLOR = {ColorMode.HS}
COLOR_MODES_HUE_EXTENDED = {ColorMode.COLOR_TEMP, ColorMode.HS}
COLOR_MODES_HUE = {
"Extended color light": COLOR_MODES_HUE_EXTENDED,
"Color light": COLOR_MODES_HUE_COLOR,
"Dimmable light": COLOR_MODES_HUE_DIMMABLE,
"On/Off plug-in unit": COLOR_MODES_HUE_ON_OFF,
"Color temperature light": COLOR_MODES_HUE_COLOR_TEMP,
}
SUPPORT_HUE_ON_OFF = SUPPORT_FLASH | SUPPORT_TRANSITION
SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF | SUPPORT_BRIGHTNESS
SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE | SUPPORT_COLOR_TEMP
SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT | SUPPORT_COLOR
SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF
SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE
SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT
SUPPORT_HUE_EXTENDED = SUPPORT_HUE_COLOR_TEMP | SUPPORT_HUE_COLOR
SUPPORT_HUE = {
@ -96,17 +109,32 @@ def create_light(item_class, coordinator, bridge, is_group, rooms, api, item_id)
api_item = api[item_id]
if is_group:
supported_color_modes = set()
supported_features = 0
for light_id in api_item.lights:
if light_id not in bridge.api.lights:
continue
light = bridge.api.lights[light_id]
supported_features |= SUPPORT_HUE.get(light.type, SUPPORT_HUE_EXTENDED)
supported_color_modes.update(
COLOR_MODES_HUE.get(light.type, COLOR_MODES_HUE_EXTENDED)
)
supported_features = supported_features or SUPPORT_HUE_EXTENDED
supported_color_modes = supported_color_modes or COLOR_MODES_HUE_EXTENDED
supported_color_modes = filter_supported_color_modes(supported_color_modes)
else:
supported_color_modes = COLOR_MODES_HUE.get(
api_item.type, COLOR_MODES_HUE_EXTENDED
)
supported_features = SUPPORT_HUE.get(api_item.type, SUPPORT_HUE_EXTENDED)
return item_class(
coordinator, bridge, is_group, api_item, supported_features, rooms
coordinator,
bridge,
is_group,
api_item,
supported_color_modes,
supported_features,
rooms,
)
@ -281,18 +309,34 @@ def hass_to_hue_brightness(value):
class HueLight(CoordinatorEntity, LightEntity):
"""Representation of a Hue light."""
def __init__(self, coordinator, bridge, is_group, light, supported_features, rooms):
def __init__(
self,
coordinator,
bridge,
is_group,
light,
supported_color_modes,
supported_features,
rooms,
):
"""Initialize the light."""
super().__init__(coordinator)
self._attr_supported_color_modes = supported_color_modes
self._attr_supported_features = supported_features
self.light = light
self.bridge = bridge
self.is_group = is_group
self._supported_features = supported_features
self._rooms = rooms
self.allow_unreachable = self.bridge.config_entry.options.get(
CONF_ALLOW_UNREACHABLE, DEFAULT_ALLOW_UNREACHABLE
)
self._fixed_color_mode = None
if len(supported_color_modes) == 1:
self._fixed_color_mode = next(iter(supported_color_modes))
else:
assert supported_color_modes == {ColorMode.COLOR_TEMP, ColorMode.HS}
if is_group:
self.is_osram = False
self.is_philips = False
@ -354,6 +398,19 @@ class HueLight(CoordinatorEntity, LightEntity):
return hue_brightness_to_hass(bri)
@property
def color_mode(self) -> str:
"""Return the color mode of the light."""
if self._fixed_color_mode:
return self._fixed_color_mode
# The light supports both hs/xy and white with adjustabe color_temperature
mode = self._color_mode
if mode in ("xy", "hs"):
return ColorMode.HS
return ColorMode.COLOR_TEMP
@property
def _color_mode(self):
"""Return the hue color mode."""
@ -426,11 +483,6 @@ class HueLight(CoordinatorEntity, LightEntity):
self.is_group or self.allow_unreachable or self.light.state["reachable"]
)
@property
def supported_features(self):
"""Flag supported features."""
return self._supported_features
@property
def effect(self):
"""Return the current effect."""

View File

@ -116,6 +116,23 @@ COLOR_MODES_COLOR = {
}
def filter_supported_color_modes(color_modes: Iterable[ColorMode]) -> set[ColorMode]:
"""Filter the given color modes."""
color_modes = set(color_modes)
if (
not color_modes
or ColorMode.UNKNOWN in color_modes
or (ColorMode.WHITE in color_modes and not color_supported(color_modes))
):
raise HomeAssistantError
if ColorMode.ONOFF in color_modes and len(color_modes) > 1:
color_modes.remove(ColorMode.ONOFF)
if ColorMode.BRIGHTNESS in color_modes and len(color_modes) > 1:
color_modes.remove(ColorMode.BRIGHTNESS)
return color_modes
def valid_supported_color_modes(
color_modes: Iterable[ColorMode | str],
) -> set[ColorMode | str]:

View File

@ -7,6 +7,7 @@ import aiohue
from homeassistant.components import hue
from homeassistant.components.hue.const import CONF_ALLOW_HUE_GROUPS
from homeassistant.components.hue.v1 import light as hue_light
from homeassistant.components.light import COLOR_MODE_COLOR_TEMP, COLOR_MODE_HS
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.util import color
@ -236,6 +237,11 @@ async def test_lights_color_mode(hass, mock_bridge_v1):
assert lamp_1.attributes["brightness"] == 145
assert lamp_1.attributes["hs_color"] == (36.067, 69.804)
assert "color_temp" not in lamp_1.attributes
assert lamp_1.attributes["color_mode"] == COLOR_MODE_HS
assert lamp_1.attributes["supported_color_modes"] == [
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
]
new_light1_on = LIGHT_1_ON.copy()
new_light1_on["state"] = new_light1_on["state"].copy()
@ -256,6 +262,11 @@ async def test_lights_color_mode(hass, mock_bridge_v1):
assert lamp_1.attributes["brightness"] == 145
assert lamp_1.attributes["color_temp"] == 467
assert "hs_color" in lamp_1.attributes
assert lamp_1.attributes["color_mode"] == COLOR_MODE_COLOR_TEMP
assert lamp_1.attributes["supported_color_modes"] == [
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
]
async def test_groups(hass, mock_bridge_v1):
@ -651,6 +662,7 @@ def test_available():
bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})),
coordinator=Mock(last_update_success=True),
is_group=False,
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
rooms={},
)
@ -666,6 +678,7 @@ def test_available():
),
coordinator=Mock(last_update_success=True),
is_group=False,
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
rooms={},
bridge=Mock(config_entry=Mock(options={"allow_unreachable": True})),
@ -682,6 +695,7 @@ def test_available():
),
coordinator=Mock(last_update_success=True),
is_group=True,
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
rooms={},
bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})),
@ -702,6 +716,7 @@ def test_hs_color():
coordinator=Mock(last_update_success=True),
bridge=Mock(),
is_group=False,
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
rooms={},
)
@ -718,6 +733,7 @@ def test_hs_color():
coordinator=Mock(last_update_success=True),
bridge=Mock(),
is_group=False,
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
rooms={},
)
@ -734,6 +750,7 @@ def test_hs_color():
coordinator=Mock(last_update_success=True),
bridge=Mock(),
is_group=False,
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
rooms={},
)
@ -910,15 +927,20 @@ async def test_group_features(hass, mock_bridge_v1):
assert len(mock_bridge_v1.mock_requests) == 2
color_temp_feature = hue_light.SUPPORT_HUE["Color temperature light"]
color_temp_mode = sorted(hue_light.COLOR_MODES_HUE["Color temperature light"])
extended_color_feature = hue_light.SUPPORT_HUE["Extended color light"]
extended_color_mode = sorted(hue_light.COLOR_MODES_HUE["Extended color light"])
group_1 = hass.states.get("light.group_1")
assert group_1.attributes["supported_color_modes"] == color_temp_mode
assert group_1.attributes["supported_features"] == color_temp_feature
group_2 = hass.states.get("light.living_room")
assert group_2.attributes["supported_color_modes"] == extended_color_mode
assert group_2.attributes["supported_features"] == extended_color_feature
group_3 = hass.states.get("light.dining_room")
assert group_3.attributes["supported_color_modes"] == extended_color_mode
assert group_3.attributes["supported_features"] == extended_color_feature
entity_registry = er.async_get(hass)

View File

@ -16,7 +16,7 @@ from homeassistant.const import (
STATE_OFF,
STATE_ON,
)
from homeassistant.exceptions import Unauthorized
from homeassistant.exceptions import HomeAssistantError, Unauthorized
from homeassistant.setup import async_setup_component
import homeassistant.util.color as color_util
@ -2417,3 +2417,44 @@ def test_valid_supported_color_modes():
supported = {light.ColorMode.BRIGHTNESS, light.ColorMode.COLOR_TEMP}
with pytest.raises(vol.Error):
light.valid_supported_color_modes(supported)
def test_filter_supported_color_modes():
"""Test filter_supported_color_modes."""
supported = {light.ColorMode.HS}
assert light.filter_supported_color_modes(supported) == supported
# Supported color modes must not be empty
supported = set()
with pytest.raises(HomeAssistantError):
light.filter_supported_color_modes(supported)
# ColorMode.WHITE must be combined with a color mode supporting color
supported = {light.ColorMode.WHITE}
with pytest.raises(HomeAssistantError):
light.filter_supported_color_modes(supported)
supported = {light.ColorMode.WHITE, light.ColorMode.COLOR_TEMP}
with pytest.raises(HomeAssistantError):
light.filter_supported_color_modes(supported)
supported = {light.ColorMode.WHITE, light.ColorMode.HS}
assert light.filter_supported_color_modes(supported) == supported
# ColorMode.ONOFF will be removed if combined with other modes
supported = {light.ColorMode.ONOFF}
assert light.filter_supported_color_modes(supported) == supported
supported = {light.ColorMode.ONOFF, light.ColorMode.COLOR_TEMP}
assert light.filter_supported_color_modes(supported) == {light.ColorMode.COLOR_TEMP}
# ColorMode.BRIGHTNESS will be removed if combined with other modes
supported = {light.ColorMode.BRIGHTNESS}
assert light.filter_supported_color_modes(supported) == supported
supported = {light.ColorMode.BRIGHTNESS, light.ColorMode.COLOR_TEMP}
assert light.filter_supported_color_modes(supported) == {light.ColorMode.COLOR_TEMP}
# ColorMode.BRIGHTNESS has priority over ColorMode.ONOFF
supported = {light.ColorMode.ONOFF, light.ColorMode.BRIGHTNESS}
assert light.filter_supported_color_modes(supported) == {light.ColorMode.BRIGHTNESS}