mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00: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,
|
EFFECT_RANDOM,
|
||||||
FLASH_LONG,
|
FLASH_LONG,
|
||||||
FLASH_SHORT,
|
FLASH_SHORT,
|
||||||
SUPPORT_BRIGHTNESS,
|
|
||||||
SUPPORT_COLOR,
|
|
||||||
SUPPORT_COLOR_TEMP,
|
|
||||||
SUPPORT_EFFECT,
|
SUPPORT_EFFECT,
|
||||||
SUPPORT_FLASH,
|
SUPPORT_FLASH,
|
||||||
SUPPORT_TRANSITION,
|
SUPPORT_TRANSITION,
|
||||||
|
ColorMode,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
|
filter_supported_color_modes,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
@ -60,10 +59,24 @@ SCAN_INTERVAL = timedelta(seconds=5)
|
|||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
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_ON_OFF = SUPPORT_FLASH | SUPPORT_TRANSITION
|
||||||
SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF | SUPPORT_BRIGHTNESS
|
SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF
|
||||||
SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE | SUPPORT_COLOR_TEMP
|
SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE
|
||||||
SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT | SUPPORT_COLOR
|
SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT
|
||||||
SUPPORT_HUE_EXTENDED = SUPPORT_HUE_COLOR_TEMP | SUPPORT_HUE_COLOR
|
SUPPORT_HUE_EXTENDED = SUPPORT_HUE_COLOR_TEMP | SUPPORT_HUE_COLOR
|
||||||
|
|
||||||
SUPPORT_HUE = {
|
SUPPORT_HUE = {
|
||||||
@ -96,17 +109,32 @@ def create_light(item_class, coordinator, bridge, is_group, rooms, api, item_id)
|
|||||||
api_item = api[item_id]
|
api_item = api[item_id]
|
||||||
|
|
||||||
if is_group:
|
if is_group:
|
||||||
|
supported_color_modes = set()
|
||||||
supported_features = 0
|
supported_features = 0
|
||||||
for light_id in api_item.lights:
|
for light_id in api_item.lights:
|
||||||
if light_id not in bridge.api.lights:
|
if light_id not in bridge.api.lights:
|
||||||
continue
|
continue
|
||||||
light = bridge.api.lights[light_id]
|
light = bridge.api.lights[light_id]
|
||||||
supported_features |= SUPPORT_HUE.get(light.type, SUPPORT_HUE_EXTENDED)
|
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_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:
|
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)
|
supported_features = SUPPORT_HUE.get(api_item.type, SUPPORT_HUE_EXTENDED)
|
||||||
return item_class(
|
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):
|
class HueLight(CoordinatorEntity, LightEntity):
|
||||||
"""Representation of a Hue light."""
|
"""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."""
|
"""Initialize the light."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
self._attr_supported_color_modes = supported_color_modes
|
||||||
|
self._attr_supported_features = supported_features
|
||||||
self.light = light
|
self.light = light
|
||||||
self.bridge = bridge
|
self.bridge = bridge
|
||||||
self.is_group = is_group
|
self.is_group = is_group
|
||||||
self._supported_features = supported_features
|
|
||||||
self._rooms = rooms
|
self._rooms = rooms
|
||||||
self.allow_unreachable = self.bridge.config_entry.options.get(
|
self.allow_unreachable = self.bridge.config_entry.options.get(
|
||||||
CONF_ALLOW_UNREACHABLE, DEFAULT_ALLOW_UNREACHABLE
|
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:
|
if is_group:
|
||||||
self.is_osram = False
|
self.is_osram = False
|
||||||
self.is_philips = False
|
self.is_philips = False
|
||||||
@ -354,6 +398,19 @@ class HueLight(CoordinatorEntity, LightEntity):
|
|||||||
|
|
||||||
return hue_brightness_to_hass(bri)
|
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
|
@property
|
||||||
def _color_mode(self):
|
def _color_mode(self):
|
||||||
"""Return the hue color mode."""
|
"""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"]
|
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
|
@property
|
||||||
def effect(self):
|
def effect(self):
|
||||||
"""Return the current effect."""
|
"""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(
|
def valid_supported_color_modes(
|
||||||
color_modes: Iterable[ColorMode | str],
|
color_modes: Iterable[ColorMode | str],
|
||||||
) -> set[ColorMode | str]:
|
) -> set[ColorMode | str]:
|
||||||
|
@ -7,6 +7,7 @@ import aiohue
|
|||||||
from homeassistant.components import hue
|
from homeassistant.components import hue
|
||||||
from homeassistant.components.hue.const import CONF_ALLOW_HUE_GROUPS
|
from homeassistant.components.hue.const import CONF_ALLOW_HUE_GROUPS
|
||||||
from homeassistant.components.hue.v1 import light as hue_light
|
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.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.util import color
|
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["brightness"] == 145
|
||||||
assert lamp_1.attributes["hs_color"] == (36.067, 69.804)
|
assert lamp_1.attributes["hs_color"] == (36.067, 69.804)
|
||||||
assert "color_temp" not in lamp_1.attributes
|
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 = LIGHT_1_ON.copy()
|
||||||
new_light1_on["state"] = new_light1_on["state"].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["brightness"] == 145
|
||||||
assert lamp_1.attributes["color_temp"] == 467
|
assert lamp_1.attributes["color_temp"] == 467
|
||||||
assert "hs_color" in lamp_1.attributes
|
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):
|
async def test_groups(hass, mock_bridge_v1):
|
||||||
@ -651,6 +662,7 @@ def test_available():
|
|||||||
bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})),
|
bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})),
|
||||||
coordinator=Mock(last_update_success=True),
|
coordinator=Mock(last_update_success=True),
|
||||||
is_group=False,
|
is_group=False,
|
||||||
|
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
|
||||||
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
||||||
rooms={},
|
rooms={},
|
||||||
)
|
)
|
||||||
@ -666,6 +678,7 @@ def test_available():
|
|||||||
),
|
),
|
||||||
coordinator=Mock(last_update_success=True),
|
coordinator=Mock(last_update_success=True),
|
||||||
is_group=False,
|
is_group=False,
|
||||||
|
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
|
||||||
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
||||||
rooms={},
|
rooms={},
|
||||||
bridge=Mock(config_entry=Mock(options={"allow_unreachable": True})),
|
bridge=Mock(config_entry=Mock(options={"allow_unreachable": True})),
|
||||||
@ -682,6 +695,7 @@ def test_available():
|
|||||||
),
|
),
|
||||||
coordinator=Mock(last_update_success=True),
|
coordinator=Mock(last_update_success=True),
|
||||||
is_group=True,
|
is_group=True,
|
||||||
|
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
|
||||||
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
||||||
rooms={},
|
rooms={},
|
||||||
bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})),
|
bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})),
|
||||||
@ -702,6 +716,7 @@ def test_hs_color():
|
|||||||
coordinator=Mock(last_update_success=True),
|
coordinator=Mock(last_update_success=True),
|
||||||
bridge=Mock(),
|
bridge=Mock(),
|
||||||
is_group=False,
|
is_group=False,
|
||||||
|
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
|
||||||
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
||||||
rooms={},
|
rooms={},
|
||||||
)
|
)
|
||||||
@ -718,6 +733,7 @@ def test_hs_color():
|
|||||||
coordinator=Mock(last_update_success=True),
|
coordinator=Mock(last_update_success=True),
|
||||||
bridge=Mock(),
|
bridge=Mock(),
|
||||||
is_group=False,
|
is_group=False,
|
||||||
|
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
|
||||||
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
||||||
rooms={},
|
rooms={},
|
||||||
)
|
)
|
||||||
@ -734,6 +750,7 @@ def test_hs_color():
|
|||||||
coordinator=Mock(last_update_success=True),
|
coordinator=Mock(last_update_success=True),
|
||||||
bridge=Mock(),
|
bridge=Mock(),
|
||||||
is_group=False,
|
is_group=False,
|
||||||
|
supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED,
|
||||||
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
supported_features=hue_light.SUPPORT_HUE_EXTENDED,
|
||||||
rooms={},
|
rooms={},
|
||||||
)
|
)
|
||||||
@ -910,15 +927,20 @@ async def test_group_features(hass, mock_bridge_v1):
|
|||||||
assert len(mock_bridge_v1.mock_requests) == 2
|
assert len(mock_bridge_v1.mock_requests) == 2
|
||||||
|
|
||||||
color_temp_feature = hue_light.SUPPORT_HUE["Color temperature light"]
|
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_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")
|
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
|
assert group_1.attributes["supported_features"] == color_temp_feature
|
||||||
|
|
||||||
group_2 = hass.states.get("light.living_room")
|
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
|
assert group_2.attributes["supported_features"] == extended_color_feature
|
||||||
|
|
||||||
group_3 = hass.states.get("light.dining_room")
|
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
|
assert group_3.attributes["supported_features"] == extended_color_feature
|
||||||
|
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
|
@ -16,7 +16,7 @@ from homeassistant.const import (
|
|||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import Unauthorized
|
from homeassistant.exceptions import HomeAssistantError, Unauthorized
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.color as color_util
|
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}
|
supported = {light.ColorMode.BRIGHTNESS, light.ColorMode.COLOR_TEMP}
|
||||||
with pytest.raises(vol.Error):
|
with pytest.raises(vol.Error):
|
||||||
light.valid_supported_color_modes(supported)
|
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…
x
Reference in New Issue
Block a user