Filter unsupported parameters from light service calls (#51084)
* Filter unsupported parameters from light service calls * Silence pylint * Fix deconz tests * Fix mqtt tests * Fix scene tests * Fix trådfri emulated CT * Fix mqtt tests
This commit is contained in:
parent
9b13350b01
commit
154c849eac
6 changed files with 243 additions and 15 deletions
|
@ -247,11 +247,52 @@ def preprocess_turn_on_alternatives(hass, params):
|
|||
params[ATTR_BRIGHTNESS] = round(255 * brightness_pct / 100)
|
||||
|
||||
|
||||
def filter_turn_off_params(params):
|
||||
def filter_turn_off_params(light, params):
|
||||
"""Filter out params not used in turn off."""
|
||||
supported_features = light.supported_features
|
||||
|
||||
if not supported_features & SUPPORT_FLASH:
|
||||
params.pop(ATTR_FLASH, None)
|
||||
if not supported_features & SUPPORT_TRANSITION:
|
||||
params.pop(ATTR_TRANSITION, None)
|
||||
|
||||
return {k: v for k, v in params.items() if k in (ATTR_TRANSITION, ATTR_FLASH)}
|
||||
|
||||
|
||||
def filter_turn_on_params(light, params):
|
||||
"""Filter out params not used in turn off."""
|
||||
supported_features = light.supported_features
|
||||
|
||||
if not supported_features & SUPPORT_EFFECT:
|
||||
params.pop(ATTR_EFFECT, None)
|
||||
if not supported_features & SUPPORT_FLASH:
|
||||
params.pop(ATTR_FLASH, None)
|
||||
if not supported_features & SUPPORT_TRANSITION:
|
||||
params.pop(ATTR_TRANSITION, None)
|
||||
if not supported_features & SUPPORT_WHITE_VALUE:
|
||||
params.pop(ATTR_WHITE_VALUE, None)
|
||||
|
||||
supported_color_modes = (
|
||||
light._light_internal_supported_color_modes # pylint:disable=protected-access
|
||||
)
|
||||
if not brightness_supported(supported_color_modes):
|
||||
params.pop(ATTR_BRIGHTNESS, None)
|
||||
if COLOR_MODE_COLOR_TEMP not in supported_color_modes:
|
||||
params.pop(ATTR_COLOR_TEMP, None)
|
||||
if COLOR_MODE_HS not in supported_color_modes:
|
||||
params.pop(ATTR_HS_COLOR, None)
|
||||
if COLOR_MODE_RGB not in supported_color_modes:
|
||||
params.pop(ATTR_RGB_COLOR, None)
|
||||
if COLOR_MODE_RGBW not in supported_color_modes:
|
||||
params.pop(ATTR_RGBW_COLOR, None)
|
||||
if COLOR_MODE_RGBWW not in supported_color_modes:
|
||||
params.pop(ATTR_RGBWW_COLOR, None)
|
||||
if COLOR_MODE_XY not in supported_color_modes:
|
||||
params.pop(ATTR_XY_COLOR, None)
|
||||
|
||||
return params
|
||||
|
||||
|
||||
async def async_setup(hass, config): # noqa: C901
|
||||
"""Expose light control via state machine and services."""
|
||||
component = hass.data[DOMAIN] = EntityComponent(
|
||||
|
@ -373,7 +414,7 @@ async def async_setup(hass, config): # noqa: C901
|
|||
if params.get(ATTR_BRIGHTNESS) == 0:
|
||||
await async_handle_light_off_service(light, call)
|
||||
else:
|
||||
await light.async_turn_on(**params)
|
||||
await light.async_turn_on(**filter_turn_on_params(light, params))
|
||||
|
||||
async def async_handle_light_off_service(light, call):
|
||||
"""Handle turning off a light."""
|
||||
|
@ -382,7 +423,7 @@ async def async_setup(hass, config): # noqa: C901
|
|||
if ATTR_TRANSITION not in params:
|
||||
profiles.apply_default(light.entity_id, True, params)
|
||||
|
||||
await light.async_turn_off(**filter_turn_off_params(params))
|
||||
await light.async_turn_off(**filter_turn_off_params(light, params))
|
||||
|
||||
async def async_handle_toggle_service(light, call):
|
||||
"""Handle toggling a light."""
|
||||
|
|
|
@ -116,7 +116,7 @@ class TradfriLight(TradfriBaseDevice, LightEntity):
|
|||
if device.light_control.can_set_dimmer:
|
||||
_features |= SUPPORT_BRIGHTNESS
|
||||
if device.light_control.can_set_color:
|
||||
_features |= SUPPORT_COLOR
|
||||
_features |= SUPPORT_COLOR | SUPPORT_COLOR_TEMP
|
||||
if device.light_control.can_set_temp:
|
||||
_features |= SUPPORT_COLOR_TEMP
|
||||
self._features = _features
|
||||
|
|
|
@ -179,7 +179,6 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
|
|||
blocking=True,
|
||||
)
|
||||
assert aioclient_mock.mock_calls[1][2] == {
|
||||
"ct": 2500,
|
||||
"bri": 200,
|
||||
"transitiontime": 50,
|
||||
"alert": "select",
|
||||
|
|
|
@ -117,6 +117,16 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
ent1, ent2, ent3 = platform.ENTITIES
|
||||
ent1.supported_color_modes = [light.COLOR_MODE_HS]
|
||||
ent3.supported_color_modes = [light.COLOR_MODE_HS]
|
||||
ent1.supported_features = light.SUPPORT_TRANSITION
|
||||
ent2.supported_features = (
|
||||
light.SUPPORT_COLOR
|
||||
| light.SUPPORT_EFFECT
|
||||
| light.SUPPORT_TRANSITION
|
||||
| light.SUPPORT_WHITE_VALUE
|
||||
)
|
||||
ent3.supported_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
|
||||
|
||||
# Test init
|
||||
assert light.is_on(hass, ent1.entity_id)
|
||||
|
@ -205,6 +215,7 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent2.entity_id,
|
||||
light.ATTR_EFFECT: "fun_effect",
|
||||
light.ATTR_RGB_COLOR: (255, 255, 255),
|
||||
light.ATTR_WHITE_VALUE: 255,
|
||||
},
|
||||
|
@ -215,6 +226,7 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent3.entity_id,
|
||||
light.ATTR_FLASH: "short",
|
||||
light.ATTR_XY_COLOR: (0.4, 0.6),
|
||||
},
|
||||
blocking=True,
|
||||
|
@ -228,10 +240,14 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||
}
|
||||
|
||||
_, data = ent2.last_call("turn_on")
|
||||
assert data == {light.ATTR_HS_COLOR: (0, 0), light.ATTR_WHITE_VALUE: 255}
|
||||
assert data == {
|
||||
light.ATTR_EFFECT: "fun_effect",
|
||||
light.ATTR_HS_COLOR: (0, 0),
|
||||
light.ATTR_WHITE_VALUE: 255,
|
||||
}
|
||||
|
||||
_, data = ent3.last_call("turn_on")
|
||||
assert data == {light.ATTR_HS_COLOR: (71.059, 100)}
|
||||
assert data == {light.ATTR_FLASH: "short", light.ATTR_HS_COLOR: (71.059, 100)}
|
||||
|
||||
# Ensure attributes are filtered when light is turned off
|
||||
await hass.services.async_call(
|
||||
|
@ -521,6 +537,8 @@ async def test_light_profiles(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
ent1, _, _ = platform.ENTITIES
|
||||
ent1.supported_color_modes = [light.COLOR_MODE_HS]
|
||||
ent1.supported_features = light.SUPPORT_TRANSITION
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
|
@ -556,6 +574,8 @@ async def test_default_profiles_group(
|
|||
mock_light_profiles[profile.name] = profile
|
||||
|
||||
ent, _, _ = platform.ENTITIES
|
||||
ent.supported_color_modes = [light.COLOR_MODE_HS]
|
||||
ent.supported_features = light.SUPPORT_TRANSITION
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True
|
||||
)
|
||||
|
@ -661,6 +681,8 @@ async def test_default_profiles_light(
|
|||
mock_light_profiles[profile.name] = profile
|
||||
|
||||
dev = next(filter(lambda x: x.entity_id == "light.ceiling_2", platform.ENTITIES))
|
||||
dev.supported_color_modes = [light.COLOR_MODE_HS]
|
||||
dev.supported_features = light.SUPPORT_TRANSITION
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
|
@ -1625,3 +1647,157 @@ async def test_light_state_color_conversion(hass, enable_custom_integrations):
|
|||
assert state.attributes["hs_color"] == (240, 100)
|
||||
assert state.attributes["rgb_color"] == (0, 0, 255)
|
||||
assert state.attributes["xy_color"] == (0.136, 0.04)
|
||||
|
||||
|
||||
async def test_services_filter_parameters(
|
||||
hass, mock_light_profiles, enable_custom_integrations
|
||||
):
|
||||
"""Test turn_on and turn_off filters unsupported parameters."""
|
||||
platform = getattr(hass.components, "test.light")
|
||||
|
||||
platform.init()
|
||||
assert await async_setup_component(
|
||||
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
ent1, _, _ = platform.ENTITIES
|
||||
|
||||
# turn off the light by setting brightness to 0, this should work even if the light
|
||||
# doesn't support brightness
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True
|
||||
)
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: ENTITY_MATCH_ALL, light.ATTR_BRIGHTNESS: 0},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert not light.is_on(hass, ent1.entity_id)
|
||||
|
||||
# Ensure all unsupported attributes are filtered when light is turned on
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_BRIGHTNESS: 0,
|
||||
light.ATTR_EFFECT: "fun_effect",
|
||||
light.ATTR_FLASH: "short",
|
||||
light.ATTR_TRANSITION: 10,
|
||||
light.ATTR_WHITE_VALUE: 0,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_COLOR_TEMP: 153,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_HS_COLOR: (0, 0),
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_RGB_COLOR: (0, 0, 0),
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_RGBW_COLOR: (0, 0, 0, 0),
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_RGBWW_COLOR: (0, 0, 0, 0, 0),
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_XY_COLOR: (0, 0),
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
_, data = ent1.last_call("turn_on")
|
||||
assert data == {}
|
||||
|
||||
# Ensure all unsupported attributes are filtered when light is turned off
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_BRIGHTNESS: 0,
|
||||
light.ATTR_EFFECT: "fun_effect",
|
||||
light.ATTR_FLASH: "short",
|
||||
light.ATTR_TRANSITION: 10,
|
||||
light.ATTR_WHITE_VALUE: 0,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert not light.is_on(hass, ent1.entity_id)
|
||||
|
||||
_, data = ent1.last_call("turn_off")
|
||||
assert data == {}
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{
|
||||
ATTR_ENTITY_ID: ent1.entity_id,
|
||||
light.ATTR_FLASH: "short",
|
||||
light.ATTR_TRANSITION: 10,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert not light.is_on(hass, ent1.entity_id)
|
||||
|
||||
_, data = ent1.last_call("turn_off")
|
||||
assert data == {}
|
||||
|
|
|
@ -945,6 +945,7 @@ async def test_sending_hs_color(hass, mqtt_mock):
|
|||
"command_topic": "test_light_rgb/set",
|
||||
"brightness": True,
|
||||
"hs": True,
|
||||
"white_value": True,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1139,6 +1140,7 @@ async def test_sending_rgb_color_with_brightness(hass, mqtt_mock):
|
|||
"command_topic": "test_light_rgb/set",
|
||||
"brightness": True,
|
||||
"rgb": True,
|
||||
"white_value": True,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1209,6 +1211,7 @@ async def test_sending_rgb_color_with_scaled_brightness(hass, mqtt_mock):
|
|||
"brightness": True,
|
||||
"brightness_scale": 100,
|
||||
"rgb": True,
|
||||
"white_value": True,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1278,6 +1281,7 @@ async def test_sending_xy_color(hass, mqtt_mock):
|
|||
"command_topic": "test_light_rgb/set",
|
||||
"brightness": True,
|
||||
"xy": True,
|
||||
"white_value": True,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -117,6 +117,8 @@ async def test_activate_scene(hass, entities, enable_custom_integrations):
|
|||
assert light.is_on(hass, light_2.entity_id)
|
||||
assert light_2.last_call("turn_on")[1].get("brightness") == 100
|
||||
|
||||
await turn_off_lights(hass, [light_2.entity_id])
|
||||
|
||||
calls = async_mock_service(hass, "light", "turn_on")
|
||||
|
||||
await hass.services.async_call(
|
||||
|
@ -156,16 +158,22 @@ async def setup_lights(hass, entities):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
light_1, light_2 = entities
|
||||
light_1.supported_color_modes = ["brightness"]
|
||||
light_2.supported_color_modes = ["brightness"]
|
||||
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_off",
|
||||
{"entity_id": [light_1.entity_id, light_2.entity_id]},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await turn_off_lights(hass, [light_1.entity_id, light_2.entity_id])
|
||||
assert not light.is_on(hass, light_1.entity_id)
|
||||
assert not light.is_on(hass, light_2.entity_id)
|
||||
|
||||
return light_1, light_2
|
||||
|
||||
|
||||
async def turn_off_lights(hass, entity_ids):
|
||||
"""Turn lights off."""
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_off",
|
||||
{"entity_id": entity_ids},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
|
Loading…
Add table
Reference in a new issue