Migrate ZHA lighting to use newer zigpy ZCL request syntax (#77676)

* Migrate unit test to use more command definition constants

* Use keyword argument syntax for sending ZCL requests

* Ensure all ZHA unit tests pass
pull/77683/head
puddly 2022-09-01 15:32:32 -04:00 committed by GitHub
parent 57c766c03c
commit 73e26b71b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 137 deletions

View File

@ -396,12 +396,6 @@ ZHA_GW_MSG_LOG_OUTPUT = "log_output"
ZHA_GW_MSG_RAW_INIT = "raw_device_initialized"
ZHA_DEVICES_LOADED_EVENT = "zha_devices_loaded_event"
EFFECT_BLINK = 0x00
EFFECT_BREATHE = 0x01
EFFECT_OKAY = 0x02
EFFECT_DEFAULT_VARIANT = 0x00
class Strobe(t.enum8):
"""Strobe enum."""

View File

@ -17,7 +17,7 @@ import zigpy.exceptions
from zigpy.profiles import PROFILES
import zigpy.quirks
from zigpy.types.named import EUI64, NWK
from zigpy.zcl.clusters.general import Groups
from zigpy.zcl.clusters.general import Groups, Identify
import zigpy.zdo.types as zdo_types
from homeassistant.const import ATTR_COMMAND, ATTR_NAME
@ -65,8 +65,6 @@ from .const import (
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS,
CONF_ENABLE_IDENTIFY_ON_JOIN,
EFFECT_DEFAULT_VARIANT,
EFFECT_OKAY,
POWER_BATTERY_OR_UNKNOWN,
POWER_MAINS_POWERED,
SIGNAL_AVAILABLE,
@ -488,7 +486,8 @@ class ZHADevice(LogMixin):
and not self.skip_configuration
):
await self._channels.identify_ch.trigger_effect(
EFFECT_OKAY, EFFECT_DEFAULT_VARIANT
effect_id=Identify.EffectIdentifier.Okay,
effect_variant=Identify.EffectVariant.Default,
)
async def async_initialize(self, from_cache: bool = False) -> None:

View File

@ -47,9 +47,6 @@ from .core.const import (
CONF_ENABLE_ENHANCED_LIGHT_TRANSITION,
CONF_ENABLE_LIGHT_TRANSITIONING_FLAG,
DATA_ZHA,
EFFECT_BLINK,
EFFECT_BREATHE,
EFFECT_DEFAULT_VARIANT,
SIGNAL_ADD_ENTITIES,
SIGNAL_ATTR_UPDATED,
SIGNAL_SET_LEVEL,
@ -70,14 +67,11 @@ DEFAULT_EXTRA_TRANSITION_DELAY_LONG = 2.0
DEFAULT_LONG_TRANSITION_TIME = 10
DEFAULT_MIN_BRIGHTNESS = 2
UPDATE_COLORLOOP_ACTION = 0x1
UPDATE_COLORLOOP_DIRECTION = 0x2
UPDATE_COLORLOOP_TIME = 0x4
UPDATE_COLORLOOP_HUE = 0x8
FLASH_EFFECTS = {
light.FLASH_SHORT: Identify.EffectIdentifier.Blink,
light.FLASH_LONG: Identify.EffectIdentifier.Breathe,
}
FLASH_EFFECTS = {light.FLASH_SHORT: EFFECT_BLINK, light.FLASH_LONG: EFFECT_BREATHE}
UNSUPPORTED_ATTRIBUTE = 0x86
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.LIGHT)
GROUP_MATCH = functools.partial(ZHA_ENTITIES.group_match, Platform.LIGHT)
PARALLEL_UPDATES = 0
@ -157,7 +151,7 @@ class BaseLight(LogMixin, light.LightEntity):
return self._attr_state
@callback
def set_level(self, value):
def set_level(self, value: int) -> None:
"""Set the brightness of this light between 0..254.
brightness level 255 is a special value instructing the device to come
@ -275,7 +269,8 @@ class BaseLight(LogMixin, light.LightEntity):
# If the light is currently off, we first need to turn it on at a low brightness level with no transition.
# After that, we set it to the desired color/temperature with no transition.
result = await self._level_channel.move_to_level_with_on_off(
DEFAULT_MIN_BRIGHTNESS, self._DEFAULT_MIN_TRANSITION_TIME
level=DEFAULT_MIN_BRIGHTNESS,
transition_time=self._DEFAULT_MIN_TRANSITION_TIME,
)
t_log["move_to_level_with_on_off"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
@ -294,7 +289,8 @@ class BaseLight(LogMixin, light.LightEntity):
and brightness_supported(self._attr_supported_color_modes)
):
result = await self._level_channel.move_to_level_with_on_off(
level, duration
level=level,
transition_time=duration,
)
t_log["move_to_level_with_on_off"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
@ -340,7 +336,9 @@ class BaseLight(LogMixin, light.LightEntity):
if new_color_provided_while_off:
# The light is has the correct color, so we can now transition it to the correct brightness level.
result = await self._level_channel.move_to_level(level, duration)
result = await self._level_channel.move_to_level(
level=level, transition_time=duration
)
t_log["move_to_level_if_color"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
self.debug("turned on: %s", t_log)
@ -355,13 +353,15 @@ class BaseLight(LogMixin, light.LightEntity):
if effect == light.EFFECT_COLORLOOP:
result = await self._color_channel.color_loop_set(
UPDATE_COLORLOOP_ACTION
| UPDATE_COLORLOOP_DIRECTION
| UPDATE_COLORLOOP_TIME,
0x2, # start from current hue
0x1, # only support up
transition if transition else 7, # transition
0, # no hue
update_flags=(
Color.ColorLoopUpdateFlags.Action
| Color.ColorLoopUpdateFlags.Direction
| Color.ColorLoopUpdateFlags.Time
),
action=Color.ColorLoopAction.Activate_from_current_hue,
direction=Color.ColorLoopDirection.Increment,
time=transition if transition else 7,
start_hue=0,
)
t_log["color_loop_set"] = result
self._attr_effect = light.EFFECT_COLORLOOP
@ -370,18 +370,19 @@ class BaseLight(LogMixin, light.LightEntity):
and effect != light.EFFECT_COLORLOOP
):
result = await self._color_channel.color_loop_set(
UPDATE_COLORLOOP_ACTION,
0x0,
0x0,
0x0,
0x0, # update action only, action off, no dir, time, hue
update_flags=Color.ColorLoopUpdateFlags.Action,
action=Color.ColorLoopAction.Deactivate,
direction=Color.ColorLoopDirection.Decrement,
time=0,
start_hue=0,
)
t_log["color_loop_set"] = result
self._attr_effect = None
if flash is not None:
result = await self._identify_channel.trigger_effect(
FLASH_EFFECTS[flash], EFFECT_DEFAULT_VARIANT
effect_id=FLASH_EFFECTS[flash],
effect_variant=Identify.EffectVariant.Default,
)
t_log["trigger_effect"] = result
@ -400,6 +401,7 @@ class BaseLight(LogMixin, light.LightEntity):
if transition is not None
else DEFAULT_ON_OFF_TRANSITION
) + DEFAULT_EXTRA_TRANSITION_DELAY_SHORT
# Start pausing attribute report parsing
if self._zha_config_enable_light_transitioning_flag:
self.async_transition_set_flag()
@ -407,7 +409,8 @@ class BaseLight(LogMixin, light.LightEntity):
# is not none looks odd here but it will override built in bulb transition times if we pass 0 in here
if transition is not None and supports_level:
result = await self._level_channel.move_to_level_with_on_off(
0, transition * 10 or self._DEFAULT_MIN_TRANSITION_TIME
level=0,
transition_time=(transition * 10 or self._DEFAULT_MIN_TRANSITION_TIME),
)
else:
result = await self._on_off_channel.off()
@ -437,12 +440,17 @@ class BaseLight(LogMixin, light.LightEntity):
t_log,
):
"""Process ZCL color commands."""
transition_time = (
self._DEFAULT_MIN_TRANSITION_TIME
if new_color_provided_while_off
else duration
)
if temperature is not None:
result = await self._color_channel.move_to_color_temp(
temperature,
self._DEFAULT_MIN_TRANSITION_TIME
if new_color_provided_while_off
else duration,
color_temp_mireds=temperature,
transition_time=transition_time,
)
t_log["move_to_color_temp"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
@ -458,20 +466,16 @@ class BaseLight(LogMixin, light.LightEntity):
and self._color_channel.enhanced_hue_supported
):
result = await self._color_channel.enhanced_move_to_hue_and_saturation(
int(hs_color[0] * 65535 / 360),
int(hs_color[1] * 2.54),
self._DEFAULT_MIN_TRANSITION_TIME
if new_color_provided_while_off
else duration,
enhanced_hue=int(hs_color[0] * 65535 / 360),
saturation=int(hs_color[1] * 2.54),
transition_time=transition_time,
)
t_log["enhanced_move_to_hue_and_saturation"] = result
else:
result = await self._color_channel.move_to_hue_and_saturation(
int(hs_color[0] * 254 / 360),
int(hs_color[1] * 2.54),
self._DEFAULT_MIN_TRANSITION_TIME
if new_color_provided_while_off
else duration,
hue=int(hs_color[0] * 254 / 360),
saturation=int(hs_color[1] * 2.54),
transition_time=transition_time,
)
t_log["move_to_hue_and_saturation"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
@ -484,11 +488,9 @@ class BaseLight(LogMixin, light.LightEntity):
if xy_color is not None:
result = await self._color_channel.move_to_color(
int(xy_color[0] * 65535),
int(xy_color[1] * 65535),
self._DEFAULT_MIN_TRANSITION_TIME
if new_color_provided_while_off
else duration,
color_x=int(xy_color[0] * 65535),
color_y=int(xy_color[1] * 65535),
transition_time=transition_time,
)
t_log["move_to_color"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:

View File

@ -137,10 +137,12 @@ async def test_devices(
if called:
assert cluster_identify.request.call_args == mock.call(
False,
64,
cluster_identify.commands_by_name["trigger_effect"].id,
cluster_identify.commands_by_name["trigger_effect"].schema,
2,
0,
effect_id=zigpy.zcl.clusters.general.Identify.EffectIdentifier.Okay,
effect_variant=(
zigpy.zcl.clusters.general.Identify.EffectVariant.Default
),
expect_reply=True,
manufacturer=None,
tries=1,

View File

@ -33,8 +33,6 @@ from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from tests.common import async_fire_time_changed
from tests.components.zha.common import async_wait_for_updates
ON = 1
OFF = 0
IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8"
IEEE_GROUPABLE_DEVICE2 = "02:2d:6f:00:0a:90:69:e9"
IEEE_GROUPABLE_DEVICE3 = "03:2d:6f:00:0a:90:69:e7"
@ -463,10 +461,10 @@ async def test_transitions(
assert dev1_cluster_level.request.await_count == 1
assert dev1_cluster_level.request.call_args == call(
False,
4,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
50, # brightness (level in ZCL)
0, # transition time
level=50,
transition_time=0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -499,10 +497,10 @@ async def test_transitions(
assert dev1_cluster_level.request.await_count == 1
assert dev1_cluster_level.request.call_args == call(
False,
4,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
18, # brightness (level in ZCL)
30, # transition time (ZCL time in 10ths of a second)
level=18,
transition_time=30,
expect_reply=True,
manufacturer=None,
tries=1,
@ -510,10 +508,10 @@ async def test_transitions(
)
assert dev1_cluster_color.request.call_args == call(
False,
10,
dev1_cluster_color.commands_by_name["move_to_color_temp"].id,
dev1_cluster_color.commands_by_name["move_to_color_temp"].schema,
432, # color temp mireds
30.0, # transition time (ZCL time in 10ths of a second)
color_temp_mireds=432,
transition_time=30.0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -547,10 +545,10 @@ async def test_transitions(
assert dev1_cluster_level.request.await_count == 1
assert dev1_cluster_level.request.call_args == call(
False,
4,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
0, # brightness (level in ZCL)
0, # transition time (ZCL time in 10ths of a second)
level=0,
transition_time=0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -584,10 +582,10 @@ async def test_transitions(
# first it comes on with no transition at 2 brightness
assert dev1_cluster_level.request.call_args_list[0] == call(
False,
4,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
2, # brightness (level in ZCL)
0, # transition time (ZCL time in 10ths of a second)
level=2,
transition_time=0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -595,10 +593,10 @@ async def test_transitions(
)
assert dev1_cluster_color.request.call_args == call(
False,
10,
dev1_cluster_color.commands_by_name["move_to_color_temp"].id,
dev1_cluster_color.commands_by_name["move_to_color_temp"].schema,
235, # color temp mireds
0, # transition time (ZCL time in 10ths of a second) - no transition when new_color_provided_while_off
color_temp_mireds=235,
transition_time=0, # no transition when new_color_provided_while_off
expect_reply=True,
manufacturer=None,
tries=1,
@ -606,10 +604,10 @@ async def test_transitions(
)
assert dev1_cluster_level.request.call_args_list[1] == call(
False,
0,
dev1_cluster_level.commands_by_name["move_to_level"].id,
dev1_cluster_level.commands_by_name["move_to_level"].schema,
25, # brightness (level in ZCL)
10.0, # transition time (ZCL time in 10ths of a second)
level=25,
transition_time=10,
expect_reply=True,
manufacturer=None,
tries=1,
@ -668,10 +666,10 @@ async def test_transitions(
# first it comes on with no transition at 2 brightness
assert dev1_cluster_level.request.call_args_list[0] == call(
False,
4,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
2, # brightness (level in ZCL)
0, # transition time (ZCL time in 10ths of a second)
level=2,
transition_time=0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -679,10 +677,10 @@ async def test_transitions(
)
assert dev1_cluster_color.request.call_args == call(
False,
10,
dev1_cluster_color.commands_by_name["move_to_color_temp"].id,
dev1_cluster_color.commands_by_name["move_to_color_temp"].schema,
236, # color temp mireds
0, # transition time (ZCL time in 10ths of a second) - no transition when new_color_provided_while_off
color_temp_mireds=236,
transition_time=0, # no transition when new_color_provided_while_off
expect_reply=True,
manufacturer=None,
tries=1,
@ -690,10 +688,10 @@ async def test_transitions(
)
assert dev1_cluster_level.request.call_args_list[1] == call(
False,
0,
dev1_cluster_level.commands_by_name["move_to_level"].id,
dev1_cluster_level.commands_by_name["move_to_level"].schema,
25, # brightness (level in ZCL)
0, # transition time (ZCL time in 10ths of a second)
level=25,
transition_time=0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -750,7 +748,7 @@ async def test_transitions(
assert dev1_cluster_on_off.request.call_args == call(
False,
1,
dev1_cluster_on_off.commands_by_name["on"].id,
dev1_cluster_on_off.commands_by_name["on"].schema,
expect_reply=True,
manufacturer=None,
@ -760,10 +758,10 @@ async def test_transitions(
assert dev1_cluster_color.request.call_args == call(
False,
10,
dev1_cluster_color.commands_by_name["move_to_color_temp"].id,
dev1_cluster_color.commands_by_name["move_to_color_temp"].schema,
236, # color temp mireds
0, # transition time (ZCL time in 10ths of a second) - no transition when new_color_provided_while_off
color_temp_mireds=236,
transition_time=0, # no transition when new_color_provided_while_off
expect_reply=True,
manufacturer=None,
tries=1,
@ -820,10 +818,10 @@ async def test_transitions(
assert dev2_cluster_level.request.await_count == 1
assert dev2_cluster_level.request.call_args == call(
False,
4,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
100, # brightness (level in ZCL)
1, # transition time - sengled light uses default minimum
level=100,
transition_time=1, # transition time - sengled light uses default minimum
expect_reply=True,
manufacturer=None,
tries=1,
@ -878,10 +876,10 @@ async def test_transitions(
# first it comes on with no transition at 2 brightness
assert dev2_cluster_level.request.call_args_list[0] == call(
False,
4,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
2, # brightness (level in ZCL)
1, # transition time (ZCL time in 10ths of a second)
level=2,
transition_time=1,
expect_reply=True,
manufacturer=None,
tries=1,
@ -889,10 +887,10 @@ async def test_transitions(
)
assert dev2_cluster_color.request.call_args == call(
False,
10,
dev2_cluster_color.commands_by_name["move_to_color_temp"].id,
dev2_cluster_color.commands_by_name["move_to_color_temp"].schema,
235, # color temp mireds
1, # transition time (ZCL time in 10ths of a second) - sengled transition == 1 when new_color_provided_while_off
color_temp_mireds=235,
transition_time=1, # sengled transition == 1 when new_color_provided_while_off
expect_reply=True,
manufacturer=None,
tries=1,
@ -900,10 +898,10 @@ async def test_transitions(
)
assert dev2_cluster_level.request.call_args_list[1] == call(
False,
0,
dev2_cluster_level.commands_by_name["move_to_level"].id,
dev2_cluster_level.commands_by_name["move_to_level"].schema,
25, # brightness (level in ZCL)
10.0, # transition time (ZCL time in 10ths of a second)
level=25,
transition_time=10,
expect_reply=True,
manufacturer=None,
tries=1,
@ -965,10 +963,10 @@ async def test_transitions(
# groups are omitted from the 3 call dance for new_color_provided_while_off
assert group_color_channel.request.call_args == call(
False,
10,
dev2_cluster_color.commands_by_name["move_to_color_temp"].id,
dev2_cluster_color.commands_by_name["move_to_color_temp"].schema,
235, # color temp mireds
10.0, # transition time (ZCL time in 10ths of a second) - sengled transition == 1 when new_color_provided_while_off
color_temp_mireds=235,
transition_time=10, # sengled transition == 1 when new_color_provided_while_off
expect_reply=True,
manufacturer=None,
tries=1,
@ -976,10 +974,10 @@ async def test_transitions(
)
assert group_level_channel.request.call_args == call(
False,
4,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
25, # brightness (level in ZCL)
10.0, # transition time (ZCL time in 10ths of a second)
level=25,
transition_time=10,
expect_reply=True,
manufacturer=None,
tries=1,
@ -1031,10 +1029,10 @@ async def test_transitions(
assert dev2_cluster_level.request.await_count == 1
assert dev2_cluster_level.request.call_args == call(
False,
4,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
0, # brightness (level in ZCL)
20, # transition time
level=0,
transition_time=20, # transition time
expect_reply=True,
manufacturer=None,
tries=1,
@ -1061,10 +1059,10 @@ async def test_transitions(
assert dev2_cluster_level.request.await_count == 1
assert dev2_cluster_level.request.call_args == call(
False,
4,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev2_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
25, # brightness (level in ZCL) - this is the last brightness we set a few tests above
1, # transition time - sengled light uses default minimum
level=25,
transition_time=1, # transition time - sengled light uses default minimum
expect_reply=True,
manufacturer=None,
tries=1,
@ -1096,7 +1094,7 @@ async def test_transitions(
# first it comes on
assert eWeLink_cluster_on_off.request.call_args_list[0] == call(
False,
1,
eWeLink_cluster_on_off.commands_by_name["on"].id,
eWeLink_cluster_on_off.commands_by_name["on"].schema,
expect_reply=True,
manufacturer=None,
@ -1105,10 +1103,10 @@ async def test_transitions(
)
assert dev1_cluster_color.request.call_args == call(
False,
10,
dev1_cluster_color.commands_by_name["move_to_color_temp"].id,
dev1_cluster_color.commands_by_name["move_to_color_temp"].schema,
235, # color temp mireds
0, # transition time (ZCL time in 10ths of a second)
color_temp_mireds=235,
transition_time=0,
expect_reply=True,
manufacturer=None,
tries=1,
@ -1153,7 +1151,7 @@ async def async_test_on_off_from_hass(hass, cluster, entity_id):
assert cluster.request.await_count == 1
assert cluster.request.call_args == call(
False,
ON,
cluster.commands_by_name["on"].id,
cluster.commands_by_name["on"].schema,
expect_reply=True,
manufacturer=None,
@ -1176,7 +1174,7 @@ async def async_test_off_from_hass(hass, cluster, entity_id):
assert cluster.request.await_count == 1
assert cluster.request.call_args == call(
False,
OFF,
cluster.commands_by_name["off"].id,
cluster.commands_by_name["off"].schema,
expect_reply=True,
manufacturer=None,
@ -1204,7 +1202,7 @@ async def async_test_level_on_off_from_hass(
assert level_cluster.request.await_count == 0
assert on_off_cluster.request.call_args == call(
False,
ON,
on_off_cluster.commands_by_name["on"].id,
on_off_cluster.commands_by_name["on"].schema,
expect_reply=True,
manufacturer=None,
@ -1228,7 +1226,7 @@ async def async_test_level_on_off_from_hass(
assert level_cluster.request.await_count == 1
assert on_off_cluster.request.call_args == call(
False,
ON,
on_off_cluster.commands_by_name["on"].id,
on_off_cluster.commands_by_name["on"].schema,
expect_reply=True,
manufacturer=None,
@ -1237,10 +1235,10 @@ async def async_test_level_on_off_from_hass(
)
assert level_cluster.request.call_args == call(
False,
4,
level_cluster.commands_by_name["move_to_level_with_on_off"].id,
level_cluster.commands_by_name["move_to_level_with_on_off"].schema,
254,
100.0,
level=254,
transition_time=100,
expect_reply=True,
manufacturer=None,
tries=1,
@ -1262,10 +1260,10 @@ async def async_test_level_on_off_from_hass(
assert level_cluster.request.await_count == 1
assert level_cluster.request.call_args == call(
False,
4,
level_cluster.commands_by_name["move_to_level_with_on_off"].id,
level_cluster.commands_by_name["move_to_level_with_on_off"].schema,
10,
expected_default_transition,
level=10,
transition_time=int(expected_default_transition),
expect_reply=True,
manufacturer=None,
tries=1,
@ -1305,10 +1303,10 @@ async def async_test_flash_from_hass(hass, cluster, entity_id, flash):
assert cluster.request.await_count == 1
assert cluster.request.call_args == call(
False,
64,
cluster.commands_by_name["trigger_effect"].id,
cluster.commands_by_name["trigger_effect"].schema,
FLASH_EFFECTS[flash],
0,
effect_id=FLASH_EFFECTS[flash],
effect_variant=general.Identify.EffectVariant.Default,
expect_reply=True,
manufacturer=None,
tries=1,