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:
parent
59c6282c6c
commit
573e966d74
@ -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."""
|
||||
|
@ -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]:
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user