Skip to content

Conversation

@hcarter-775
Copy link
Contributor

@hcarter-775 hcarter-775 commented Dec 29, 2025

Description of Change

Update all profiles to support statelessStep capabilities as needed. Include handlers for these step capabilities using the Matter-provided step commands.

This breaks apart the work found in #2669 so that the stateless capability support can be reviewed as an isolated unit.

Summary of Completed Tests

Unit tests added. Tested on-device.

@github-actions
Copy link

Duplicate profile check: Warning - duplicate profiles detected.
light-color-level-2200K-6500K.yml == light-color-level.yml
light-level-colorTemperature-2200K-6500K.yml == light-level-colorTemperature.yml

@github-actions
Copy link

@github-actions
Copy link

github-actions bot commented Dec 29, 2025

Test Results

   71 files    481 suites   0s ⏱️
2 485 tests 2 331 ✅ 0 💤 0 ❌ 154 🔥
4 266 runs  4 027 ✅ 0 💤 0 ❌ 239 🔥

For more details on these errors, see this check.

Results for commit 5e8a5ec.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Dec 29, 2025

File Coverage
All files 42%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/embedded_cluster_utils.lua 32%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua 99%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/color_utils.lua 9%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua 18%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua 17%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_handlers/event_handlers.lua 24%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua 13%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua 19%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/init.lua 76%

Minimum allowed coverage is 90%

Generated by 🐒 cobertura-action against 5e8a5ec

@hcarter-775 hcarter-775 force-pushed the feature/statelessStep branch from 80a5c0a to 00f1bf4 Compare January 7, 2026 19:16
Copy link
Contributor

@nickolas-deboom nickolas-deboom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I approved earlier but noticed there are some print statements from debugging I assume

@hcarter-775
Copy link
Contributor Author

@nickolas-deboom thanks for the catch, got those removed

Copy link
Contributor

@nickolas-deboom nickolas-deboom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Looks like there is are test failures but I ran the tests locally and they pass, so they might need to be re-run in CI

version: 1
- id: switchLevel
version: 1
- id: statelessSwitchLevelStep
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be after the config here and in some of the other profiles

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

local COLOR_TEMPERATURE_KELVIN_MIN = 1000
SwitchFields.COLOR_TEMPERATURE_MIRED_MAX = SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN
SwitchFields.COLOR_TEMPERATURE_MIRED_MIN = SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MAX
SwitchFields.COLOR_TEMPERATURE_MIRED_MAX = math.floor(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this changing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these default values got used (which normally they would not be), COLOR_TEMPERATURE_MIRED_MAX specifically would be a decimal value while commands are expecting ints, which a unit test was complaining about. I should also note that the min/max kelvin values here were arbitrarily chosen, and I mistakenly assumed these conversions would be a clean integer division

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the step function requires that these are numbers, not floats. I could have done the floor-ing in the handler, but since they are just arbitrary constants, I figured why not do it in the base def

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed offline, changed to st_utils.round

-- [[ STATELESS SWITCH LEVEL STEP CAPABILITY COMMANDS ]] --

function CapabilityHandlers.handle_step_level(driver, device, cmd)
local step_size = math.floor((cmd.args and cmd.args.stepSize or 0)/100.0 * 254)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use round here like we do in the other level handling functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the switchLevel capability handler defined right above this uses floor as well. The attribute handlers use round though, apparently.

The break in same behavior occurred here: b52b3c0. This is not in beta yet

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a PR to change the normal switchLevel handler as well: #2690

If this one is merged soon, it can go in alongside the other commit I mentioned for the release

local endpoint_id = device:component_to_endpoint(cmd.component)
-- before the Matter 1.3 lua libs update (HUB FW 55), there was no ColorControl StepModeEnum type defined
local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.UP or 1)
local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be correct but just want to confirm, this should be COLOR_TEMP_MIN here and not COLOR_TEMP_MAX? The min mired value is the max kelvin value and vice versa

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thing to check, but pretty sure this is OK. See this logic in the bounds handler:

    switch_utils.set_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_KELVIN..minOrMax, ib.endpoint_id, temp_in_kelvin)
    -- the minimum color temp in kelvin corresponds to the maximum temp in mireds
    if minOrMax == fields.COLOR_TEMP_MIN then
      switch_utils.set_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, ib.endpoint_id, temp_in_mired)
    else
      switch_utils.set_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, ib.endpoint_id, temp_in_mired)
    end

so we set fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN as the min value for mireds, and fields.COLOR_TEMP_BOUND_RECEIVED_KELVIN..fields.COLOR_TEMP_MIN as the min value for kelvin

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes me head hurt so if you've tested and confirmed the right fields are getting set correctly that's good enough for me :)

local endpoint_id = device:component_to_endpoint(cmd.component)
local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN
print("level:", step_size)
device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tpmanley @hcarter-775 What about using StepWithOnOff to turn on the light while it's off state?
Do we plan to support the level change during the 'off' state?

and Does transition(0) is best choice for the smooth transition? I wonder this is the best value from the test with real devices.
Now, all have '0' value.
lua_libs\st\matter\defaults\colorControl.lua
local TRANSITION_TIME = 0 --1/10ths of a second
lua_libs\st\matter\defaults\colorTemperature.lua
local TRANSITION_TIME = 0 --1/10ths of a second

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It really depends on the scenario, using the normal Step and a transition time of 0 is the most sensible default.

I have been playing with all those parameters for months with my custom driver for Matter lights and multiple buttons (including the new BILRESA scroll) so I will share the experience in case it is useful.

StepWithOnOff is great for dimmers that do not have on/off dedicated buttons since otherwise you would have no way to control the on/off state. It has two main drawbacks though:

  • Cannot set the lights to the minimum brightness level. Most of the time you end up with the light turned off because it's virtually impossible to stop at the minimum. Or just impossible if the minimum is 1% and the steps are, let's say, 10%, it will go from 10% to off with the final step.
  • If you are controlling multiple lights at the same time, they will all turn on or off. As a user, most of the time I do not want that behaviour and expect to control the brightness of the lights that are on, not turn on everything.

Transition time is a great feature of Matter that should have more presence in the capabilities. setLevel of switchLevel has it but not the commands to set colours or colour temperature. It is something requested time ago and the main reason for me to use a custom driver.

When it comes to stepping the brightness level, using transitions is tricky:

  • Some lights flood the hub with brightness level reports while stepping, especially before Matter 1.4 made quiet reporting mandatory. That makes the hub sluggish for a moment and the user would feel the dimming is not responsive. With some lights a transition time of 1 (tenth of a second) the experience is fine, with others it's better to leave it at 0 since they already apply some default transition anyway.
  • If two step commands are sent too close in time and the first one did not end the transition yet, the first step is essentially killed. That's easy to experience with a BILRESA scroll since the steps can happen too fast as you scroll.

For added flexibility, the capability should have optional second and third command arguments for the transition time and the withOnOff behaviour. That would make the default 0 and normal Step if not provided. Maybe it is not late to add them being a proposed capability not yet used elsewhere.

While unrelated to the steps, adding the transition time argument to the commands to set colour and temperature would be great too, like in setLevel of switchLevel capability. It should not break anything since the default is 0 anyway.

local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds
local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0))
print("percent:", step_size_in_mireds)
device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hcarter-775 @tpmanley
Don't we need to turn on the light if the light is off state?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants