From 960ed13f9442f783ce3045acce5b64f8971faa14 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 14 May 2021 22:58:37 +0200 Subject: [PATCH] Update light device actions to check supported_color_modes (#50611) --- homeassistant/components/light/__init__.py | 6 +-- .../components/light/device_action.py | 43 ++++++++++++++++--- tests/components/light/test_device_action.py | 27 +++++++++--- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 0bc68702467..caf3ac209cb 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -95,21 +95,21 @@ def valid_supported_color_modes(color_modes: Iterable[str]) -> set[str]: return color_modes -def brightness_supported(color_modes: Iterable[str]) -> bool: +def brightness_supported(color_modes: Iterable[str] | None) -> bool: """Test if brightness is supported.""" if not color_modes: return False return any(mode in COLOR_MODES_BRIGHTNESS for mode in color_modes) -def color_supported(color_modes: Iterable[str]) -> bool: +def color_supported(color_modes: Iterable[str] | None) -> bool: """Test if color is supported.""" if not color_modes: return False return any(mode in COLOR_MODES_COLOR for mode in color_modes) -def color_temp_supported(color_modes: Iterable[str]) -> bool: +def color_temp_supported(color_modes: Iterable[str] | None) -> bool: """Test if color temperature is supported.""" if not color_modes: return False diff --git a/homeassistant/components/light/device_action.py b/homeassistant/components/light/device_action.py index 9cdb5764d70..3de2218d7c7 100644 --- a/homeassistant/components/light/device_action.py +++ b/homeassistant/components/light/device_action.py @@ -13,11 +13,17 @@ from homeassistant.components.light import ( ) from homeassistant.const import ATTR_ENTITY_ID, CONF_DOMAIN, CONF_TYPE, SERVICE_TURN_ON from homeassistant.core import Context, HomeAssistant, HomeAssistantError -from homeassistant.helpers import config_validation as cv, entity_registry +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity import get_supported_features from homeassistant.helpers.typing import ConfigType, TemplateVarsType -from . import ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRIGHTNESS +from . import ( + ATTR_BRIGHTNESS_PCT, + ATTR_BRIGHTNESS_STEP_PCT, + ATTR_SUPPORTED_COLOR_MODES, + DOMAIN, + brightness_supported, +) TYPE_BRIGHTNESS_INCREASE = "brightness_increase" TYPE_BRIGHTNESS_DECREASE = "brightness_decrease" @@ -37,6 +43,25 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( ) +def get_supported_color_modes(hass: HomeAssistant, entity_id: str) -> set | None: + """Get supported color modes for a light entity. + + First try the statemachine, then entity registry. + """ + state = hass.states.get(entity_id) + if state: + return state.attributes.get(ATTR_SUPPORTED_COLOR_MODES) + + entity_registry = er.async_get(hass) + entry = entity_registry.async_get(entity_id) + if not entry: + raise HomeAssistantError(f"Unknown entity {entity_id}") + if not entry.capabilities: + return None + + return entry.capabilities.get(ATTR_SUPPORTED_COLOR_MODES) + + async def async_call_action_from_config( hass: HomeAssistant, config: ConfigType, @@ -77,15 +102,16 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: """List device actions.""" actions = await toggle_entity.async_get_actions(hass, device_id, DOMAIN) - registry = await entity_registry.async_get_registry(hass) + entity_registry = er.async_get(hass) - for entry in entity_registry.async_entries_for_device(registry, device_id): + for entry in er.async_entries_for_device(entity_registry, device_id): if entry.domain != DOMAIN: continue + supported_color_modes = get_supported_color_modes(hass, entry.entity_id) supported_features = get_supported_features(hass, entry.entity_id) - if supported_features & SUPPORT_BRIGHTNESS: + if brightness_supported(supported_color_modes): actions.extend( ( { @@ -123,6 +149,11 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di if config[CONF_TYPE] != toggle_entity.CONF_TURN_ON: return {} + try: + supported_color_modes = get_supported_color_modes(hass, config[ATTR_ENTITY_ID]) + except HomeAssistantError: + supported_color_modes = None + try: supported_features = get_supported_features(hass, config[ATTR_ENTITY_ID]) except HomeAssistantError: @@ -130,7 +161,7 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di extra_fields = {} - if supported_features & SUPPORT_BRIGHTNESS: + if brightness_supported(supported_color_modes): extra_fields[vol.Optional(ATTR_BRIGHTNESS_PCT)] = VALID_BRIGHTNESS_PCT if supported_features & SUPPORT_FLASH: diff --git a/tests/components/light/test_device_action.py b/tests/components/light/test_device_action.py index 5d6ca2f4a2c..440cae8f0fc 100644 --- a/tests/components/light/test_device_action.py +++ b/tests/components/light/test_device_action.py @@ -3,10 +3,11 @@ import pytest import homeassistant.components.automation as automation from homeassistant.components.light import ( + ATTR_SUPPORTED_COLOR_MODES, + COLOR_MODE_BRIGHTNESS, DOMAIN, FLASH_LONG, FLASH_SHORT, - SUPPORT_BRIGHTNESS, SUPPORT_FLASH, ) from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON @@ -55,7 +56,8 @@ async def test_get_actions(hass, device_reg, entity_reg): "test", "5678", device_id=device_entry.id, - supported_features=SUPPORT_BRIGHTNESS | SUPPORT_FLASH, + supported_features=SUPPORT_FLASH, + capabilities={"supported_color_modes": ["brightness"]}, ) expected_actions = [ { @@ -132,13 +134,15 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg): @pytest.mark.parametrize( - "set_state,num_actions,supported_features_reg,supported_features_state,expected_capabilities", + "set_state,num_actions,supported_features_reg,supported_features_state,capabilities_reg,attributes_state,expected_capabilities", [ ( False, 5, - SUPPORT_BRIGHTNESS, 0, + 0, + {ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_BRIGHTNESS]}, + {}, { "turn_on": [ { @@ -155,7 +159,9 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg): True, 5, 0, - SUPPORT_BRIGHTNESS, + 0, + None, + {ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_BRIGHTNESS]}, { "turn_on": [ { @@ -173,6 +179,8 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg): 4, SUPPORT_FLASH, 0, + None, + {}, { "turn_on": [ { @@ -189,6 +197,8 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg): 4, 0, SUPPORT_FLASH, + None, + {}, { "turn_on": [ { @@ -210,6 +220,8 @@ async def test_get_action_capabilities_features( num_actions, supported_features_reg, supported_features_state, + capabilities_reg, + attributes_state, expected_capabilities, ): """Test we get the expected capabilities from a light action.""" @@ -225,10 +237,13 @@ async def test_get_action_capabilities_features( "5678", device_id=device_entry.id, supported_features=supported_features_reg, + capabilities=capabilities_reg, ).entity_id if set_state: hass.states.async_set( - entity_id, None, {"supported_features": supported_features_state} + entity_id, + None, + {"supported_features": supported_features_state, **attributes_state}, ) actions = await async_get_device_automations(hass, "action", device_entry.id)