From 6c4b11c89304e9b12048432f8e8bbbddd6a3dc2f Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 26 May 2026 14:32:05 -0500 Subject: [PATCH 1/2] add stateless refresh delay for ZLL bulbs --- .../src/stateless_handlers/init.lua | 31 ++++++- .../zigbee-switch/src/switch_utils.lua | 5 - .../src/test/test_zll_color_temp_bulb.lua | 91 +++++++++++++++++++ .../src/test/test_zll_dimmer_bulb.lua | 27 ++++++ 4 files changed, 147 insertions(+), 7 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua b/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua index 82be544f4a..9cf2894e34 100644 --- a/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua @@ -3,14 +3,39 @@ local capabilities = require "st.capabilities" local st_utils = require "st.utils" +local constants = require "st.zigbee.constants" local clusters = require "st.zigbee.zcl.clusters" local switch_utils = require "switch_utils" +-- Indicates whether a delayed refresh for ZLL devices has been triggered, to prevent multiple refreshes in a quick series of step commands +local DELAYED_REFRESH_TRIGGERED = "__delayed_refresh_triggered" + +-- delay time (in seconds) for the refresh sent for ZLL devices after a step command +local REFRESH_DELAY = "__refresh_delay" +local DEFAULT_REFRESH_DELAY = 3 + +local function trigger_delayed_refresh_if_zll(device) + if device:get_profile_id() == constants.ZLL_PROFILE_ID and not device:get_field(DELAYED_REFRESH_TRIGGERED) then + device:set_field(DELAYED_REFRESH_TRIGGERED, true) + local refresh_delay = device:get_field(REFRESH_DELAY) or DEFAULT_REFRESH_DELAY + device.thread:call_with_delay(refresh_delay, + function () + device:refresh() + device:set_field(DELAYED_REFRESH_TRIGGERED, nil) + end, + "statelessStep delayed read" + ) + end +end + -- These values are the mired versions of the config bounds in the default profile (e.g. color-temp-bulb) local DEFAULT_MIRED_MAX_BOUND = 370 -- 2700 Kelvin (Mireds are the inverse of Kelvin) local DEFAULT_MIRED_MIN_BOUND = 154 -- 6500 Kelvin (Mireds are the inverse of Kelvin) -- Transition Time: The time that shall be taken to perform the step change, in units of 1/10ths of a second. +-- Specific fields can store custom transition times for stateless capabilities +local SWITCH_LEVEL_STEP_TRANSITION_TIME = "__switch_level_step_transition_time" +local COLOR_TEMP_STEP_TRANSITION_TIME = "__color_temp_step_transition_time" local DEFAULT_STEP_TRANSITION_TIME = 3 -- 0.3 seconds -- Options Mask & Override: Indicates which options are being overridden by the Level/ColorControl cluster commands @@ -23,7 +48,7 @@ local function step_color_temperature_by_percent_handler(driver, device, cmd) end local step_percent_change = cmd.args and cmd.args.stepSize or 0 if step_percent_change == 0 then return end - local transition_time = device:get_field(switch_utils.COLOR_TEMP_STEP_TRANSITION_TIME) or DEFAULT_STEP_TRANSITION_TIME + local transition_time = device:get_field(COLOR_TEMP_STEP_TRANSITION_TIME) or DEFAULT_STEP_TRANSITION_TIME -- Reminder, stepSize > 0 == Kelvin UP == Mireds DOWN. stepSize < 0 == Kelvin DOWN == Mireds UP local step_mode = (step_percent_change > 0) and clusters.ColorControl.types.CcStepMode.DOWN or clusters.ColorControl.types.CcStepMode.UP -- note: the field containing the color temp bounds will be associated with a parent device @@ -37,6 +62,7 @@ local function step_color_temperature_by_percent_handler(driver, device, cmd) end local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, step_mode, step_size_in_mireds, transition_time, min_mireds, max_mireds, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF)) + trigger_delayed_refresh_if_zll(device) end local function step_level_handler(driver, device, cmd) @@ -45,9 +71,10 @@ local function step_level_handler(driver, device, cmd) end local step_size = st_utils.round((cmd.args and cmd.args.stepSize or 0)/100.0 * 254) if step_size == 0 then return end - local transition_time = device:get_field(switch_utils.SWITCH_LEVEL_STEP_TRANSITION_TIME) or DEFAULT_STEP_TRANSITION_TIME + local transition_time = device:get_field(SWITCH_LEVEL_STEP_TRANSITION_TIME) or DEFAULT_STEP_TRANSITION_TIME local step_mode = (step_size > 0) and clusters.Level.types.MoveStepMode.UP or clusters.Level.types.MoveStepMode.DOWN device:send(clusters.Level.server.commands.Step(device, step_mode, math.abs(step_size), transition_time, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF)) + trigger_delayed_refresh_if_zll(device) end local stateless_handlers = { diff --git a/drivers/SmartThings/zigbee-switch/src/switch_utils.lua b/drivers/SmartThings/zigbee-switch/src/switch_utils.lua index e974d52474..d30ada0588 100644 --- a/drivers/SmartThings/zigbee-switch/src/switch_utils.lua +++ b/drivers/SmartThings/zigbee-switch/src/switch_utils.lua @@ -8,11 +8,6 @@ local switch_utils = {} switch_utils.MIRED_MAX_BOUND = "__max_mired_bound" switch_utils.MIRED_MIN_BOUND = "__min_mired_bound" --- Fields to store the transition times for the stateless capabilities, --- in case native handler implementations need to be re-configured in the future -switch_utils.SWITCH_LEVEL_STEP_TRANSITION_TIME = "__switch_level_step_transition_time" -switch_utils.COLOR_TEMP_STEP_TRANSITION_TIME = "__color_temp_step_transition_time" - switch_utils.MIREDS_CONVERSION_CONSTANT = 1000000 switch_utils.convert_mired_to_kelvin = function(mired) diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua b/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua index a02e3978f5..b8cb5179c6 100644 --- a/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua +++ b/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua @@ -11,6 +11,12 @@ local OnOff = clusters.OnOff local Level = clusters.Level local ColorControl = clusters.ColorControl +local TRANSITION_TIME = 3 +local OPTIONS_MASK = 0x01 +local IGNORE_COMMAND_IF_OFF = 0x00 +local DEFAULT_MIRED_MIN = 154 +local DEFAULT_MIRED_MAX = 370 + local mock_device = test.mock_device.build_test_zigbee_device( { profile = t_utils.get_profile_definition("color-temp-bulb.yml"), fingerprinted_endpoint_id = 0x01, @@ -279,4 +285,89 @@ test.register_coroutine_test( max_api_version = 19 } ) + +test.register_coroutine_test( + "StatelessColorTemperatureStep stepColorTemperatureByPercent should trigger delayed refresh on ZLL device", + function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") + test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { 20 } } }) + mock_device:expect_native_cmd_handler_registration("statelessColorTemperatureStep", "stepColorTemperatureByPercent") + test.socket.zigbee:__expect_send({ + mock_device.id, + ColorControl.server.commands.StepColorTemperature(mock_device, ColorControl.types.CcStepMode.DOWN, 43, TRANSITION_TIME, DEFAULT_MIRED_MIN, DEFAULT_MIRED_MAX, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) + }) + + test.wait_for_events() + test.mock_time.advance_time(1) + + -- an event after 1 second should not trigger another delayed refresh + test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { 20 } } }) + mock_device:expect_native_cmd_handler_registration("statelessColorTemperatureStep", "stepColorTemperatureByPercent") + test.socket.zigbee:__expect_send({ + mock_device.id, + ColorControl.server.commands.StepColorTemperature(mock_device, ColorControl.types.CcStepMode.DOWN, 43, TRANSITION_TIME, DEFAULT_MIRED_MIN, DEFAULT_MIRED_MAX, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) + }) + + test.wait_for_events() + test.mock_time.advance_time(2) + + test.socket.zigbee:__expect_send({ mock_device.id, OnOff.attributes.OnOff:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, Level.attributes.CurrentLevel:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTemperatureMireds:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMaxMireds:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMinMireds:read(mock_device) }) + + test.wait_for_events() + test.mock_time.advance_time(1) + + -- nothing should be triggered after 1 more second + end, + { + min_api_version = 20 + } +) + +test.register_coroutine_test( + "StatelessSwitchLevelStep stepLevel should trigger delayed refresh on ZLL color temp device", + function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") + test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } }) + mock_device:expect_native_cmd_handler_registration("statelessSwitchLevelStep", "stepLevel") + test.socket.zigbee:__expect_send({ + mock_device.id, + Level.server.commands.Step(mock_device, Level.types.MoveStepMode.UP, 64, TRANSITION_TIME, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) + }) + + test.wait_for_events() + test.mock_time.advance_time(1) + + -- an event after 1 second should not trigger another delayed refresh + test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } }) + mock_device:expect_native_cmd_handler_registration("statelessSwitchLevelStep", "stepLevel") + test.socket.zigbee:__expect_send({ + mock_device.id, + Level.server.commands.Step(mock_device, Level.types.MoveStepMode.UP, 64, TRANSITION_TIME, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) + }) + + test.wait_for_events() + test.mock_time.advance_time(2) + + test.socket.zigbee:__expect_send({ mock_device.id, OnOff.attributes.OnOff:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, Level.attributes.CurrentLevel:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTemperatureMireds:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMaxMireds:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMinMireds:read(mock_device) }) + + test.wait_for_events() + test.mock_time.advance_time(1) + + -- nothing should be triggered after 1 more second + end, + { + min_api_version = 20 + } +) + test.run_registered_tests() diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua b/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua index 19d91d697a..ea34001f59 100644 --- a/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua +++ b/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua @@ -10,6 +10,10 @@ local zigbee_test_utils = require "integration_test.zigbee_test_utils" local OnOff = clusters.OnOff local Level = clusters.Level +local TRANSITION_TIME = 3 +local OPTIONS_MASK = 0x01 +local IGNORE_COMMAND_IF_OFF = 0x00 + local mock_device = test.mock_device.build_test_zigbee_device( { profile = t_utils.get_profile_definition("on-off-level.yml"), fingerprinted_endpoint_id = 0x01, @@ -178,4 +182,27 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "StatelessSwitchLevelStep stepLevel should trigger delayed refresh on ZLL device", + function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") + test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } }) + mock_device:expect_native_cmd_handler_registration("statelessSwitchLevelStep", "stepLevel") + test.socket.zigbee:__expect_send({ + mock_device.id, + Level.server.commands.Step(mock_device, Level.types.MoveStepMode.UP, 64, TRANSITION_TIME, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) + }) + + test.wait_for_events() + test.mock_time.advance_time(3) + + test.socket.zigbee:__expect_send({ mock_device.id, OnOff.attributes.OnOff:read(mock_device) }) + test.socket.zigbee:__expect_send({ mock_device.id, Level.attributes.CurrentLevel:read(mock_device) }) + end, + { + min_api_version = 19 + } +) + test.run_registered_tests() From d293dd6214aa87c5d09bfcde38ee2a7164a21ead Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 26 May 2026 15:55:52 -0500 Subject: [PATCH 2/2] update logic to cancel and reset timer --- .../src/stateless_handlers/init.lua | 44 ++++++++++--------- .../src/test/test_zll_color_temp_bulb.lua | 37 +++++----------- .../src/test/test_zll_dimmer_bulb.lua | 4 +- 3 files changed, 36 insertions(+), 49 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua b/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua index 9cf2894e34..c940e6b31f 100644 --- a/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/stateless_handlers/init.lua @@ -7,27 +7,6 @@ local constants = require "st.zigbee.constants" local clusters = require "st.zigbee.zcl.clusters" local switch_utils = require "switch_utils" --- Indicates whether a delayed refresh for ZLL devices has been triggered, to prevent multiple refreshes in a quick series of step commands -local DELAYED_REFRESH_TRIGGERED = "__delayed_refresh_triggered" - --- delay time (in seconds) for the refresh sent for ZLL devices after a step command -local REFRESH_DELAY = "__refresh_delay" -local DEFAULT_REFRESH_DELAY = 3 - -local function trigger_delayed_refresh_if_zll(device) - if device:get_profile_id() == constants.ZLL_PROFILE_ID and not device:get_field(DELAYED_REFRESH_TRIGGERED) then - device:set_field(DELAYED_REFRESH_TRIGGERED, true) - local refresh_delay = device:get_field(REFRESH_DELAY) or DEFAULT_REFRESH_DELAY - device.thread:call_with_delay(refresh_delay, - function () - device:refresh() - device:set_field(DELAYED_REFRESH_TRIGGERED, nil) - end, - "statelessStep delayed read" - ) - end -end - -- These values are the mired versions of the config bounds in the default profile (e.g. color-temp-bulb) local DEFAULT_MIRED_MAX_BOUND = 370 -- 2700 Kelvin (Mireds are the inverse of Kelvin) local DEFAULT_MIRED_MIN_BOUND = 154 -- 6500 Kelvin (Mireds are the inverse of Kelvin) @@ -42,6 +21,29 @@ local DEFAULT_STEP_TRANSITION_TIME = 3 -- 0.3 seconds local OPTIONS_MASK = 0x01 -- default: The `ExecuteIfOff` option is overriden local IGNORE_COMMAND_IF_OFF = 0x00 -- default: the command will not be executed if the device is off +-- Indicates whether a delayed refresh for ZLL devices is in progress, to prevent multiple refreshes in a quick series of step commands +local IS_REFRESH_CALLBACK_QUEUED = "__is_refresh_callback_queued" +-- Stores a timer object, which is required to cancel a timer early +local REFRESH_CALLBACK_TIMER = "__refresh_callback_timer" + +local function trigger_delayed_refresh_if_zll(device) + if device:get_profile_id() ~= constants.ZLL_PROFILE_ID then + return + end + + -- If a refresh callback is already queued, cancel it and create a new one with the updated time + if device:get_field(IS_REFRESH_CALLBACK_QUEUED) then + device.thread:cancel_timer(device:get_field(REFRESH_CALLBACK_TIMER)) + end + local delay_s = 2 + local new_timer = device.thread:call_with_delay(delay_s, function() + device:refresh() + device:set_field(IS_REFRESH_CALLBACK_QUEUED, nil) + end) + device:set_field(REFRESH_CALLBACK_TIMER, new_timer) + device:set_field(IS_REFRESH_CALLBACK_QUEUED, true) +end + local function step_color_temperature_by_percent_handler(driver, device, cmd) if type(device.register_native_capability_cmd_handler) == "function" then device:register_native_capability_cmd_handler(cmd.capability, cmd.command) diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua b/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua index b8cb5179c6..f6842b077f 100644 --- a/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua +++ b/drivers/SmartThings/zigbee-switch/src/test/test_zll_color_temp_bulb.lua @@ -290,18 +290,7 @@ test.register_coroutine_test( "StatelessColorTemperatureStep stepColorTemperatureByPercent should trigger delayed refresh on ZLL device", function() test.socket.zigbee:__set_channel_ordering("relaxed") - test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") - test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { 20 } } }) - mock_device:expect_native_cmd_handler_registration("statelessColorTemperatureStep", "stepColorTemperatureByPercent") - test.socket.zigbee:__expect_send({ - mock_device.id, - ColorControl.server.commands.StepColorTemperature(mock_device, ColorControl.types.CcStepMode.DOWN, 43, TRANSITION_TIME, DEFAULT_MIRED_MIN, DEFAULT_MIRED_MAX, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) - }) - - test.wait_for_events() - test.mock_time.advance_time(1) - - -- an event after 1 second should not trigger another delayed refresh + test.timer.__create_and_queue_test_time_advance_timer(1, "oneshot") test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { 20 } } }) mock_device:expect_native_cmd_handler_registration("statelessColorTemperatureStep", "stepColorTemperatureByPercent") test.socket.zigbee:__expect_send({ @@ -317,11 +306,6 @@ test.register_coroutine_test( test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTemperatureMireds:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMaxMireds:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMinMireds:read(mock_device) }) - - test.wait_for_events() - test.mock_time.advance_time(1) - - -- nothing should be triggered after 1 more second end, { min_api_version = 20 @@ -329,41 +313,42 @@ test.register_coroutine_test( ) test.register_coroutine_test( - "StatelessSwitchLevelStep stepLevel should trigger delayed refresh on ZLL color temp device", + "Rapid StatelessSwitchLevelStep stepLevel commands should cancel and recreate delayed refresh timer", function() test.socket.zigbee:__set_channel_ordering("relaxed") - test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") + test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } }) mock_device:expect_native_cmd_handler_registration("statelessSwitchLevelStep", "stepLevel") test.socket.zigbee:__expect_send({ mock_device.id, Level.server.commands.Step(mock_device, Level.types.MoveStepMode.UP, 64, TRANSITION_TIME, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) }) + test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") test.wait_for_events() test.mock_time.advance_time(1) - -- an event after 1 second should not trigger another delayed refresh + -- Second step command: cancels timer #1, creates timer #2 test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } }) mock_device:expect_native_cmd_handler_registration("statelessSwitchLevelStep", "stepLevel") test.socket.zigbee:__expect_send({ mock_device.id, Level.server.commands.Step(mock_device, Level.types.MoveStepMode.UP, 64, TRANSITION_TIME, OPTIONS_MASK, IGNORE_COMMAND_IF_OFF) }) + test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") test.wait_for_events() - test.mock_time.advance_time(2) + test.mock_time.advance_time(1) + -- now, nothing should happen since the first timer was cancelled and the second timer has not yet reached its 2s delay + + test.wait_for_events() + test.mock_time.advance_time(1) test.socket.zigbee:__expect_send({ mock_device.id, OnOff.attributes.OnOff:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, Level.attributes.CurrentLevel:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTemperatureMireds:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMaxMireds:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, ColorControl.attributes.ColorTempPhysicalMinMireds:read(mock_device) }) - - test.wait_for_events() - test.mock_time.advance_time(1) - - -- nothing should be triggered after 1 more second end, { min_api_version = 20 diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua b/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua index ea34001f59..ec10a66a62 100644 --- a/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua +++ b/drivers/SmartThings/zigbee-switch/src/test/test_zll_dimmer_bulb.lua @@ -186,7 +186,7 @@ test.register_coroutine_test( "StatelessSwitchLevelStep stepLevel should trigger delayed refresh on ZLL device", function() test.socket.zigbee:__set_channel_ordering("relaxed") - test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") + test.timer.__create_and_queue_test_time_advance_timer(1, "oneshot") test.socket.capability:__queue_receive({ mock_device.id, { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } }) mock_device:expect_native_cmd_handler_registration("statelessSwitchLevelStep", "stepLevel") test.socket.zigbee:__expect_send({ @@ -195,7 +195,7 @@ test.register_coroutine_test( }) test.wait_for_events() - test.mock_time.advance_time(3) + test.mock_time.advance_time(2) test.socket.zigbee:__expect_send({ mock_device.id, OnOff.attributes.OnOff:read(mock_device) }) test.socket.zigbee:__expect_send({ mock_device.id, Level.attributes.CurrentLevel:read(mock_device) })