Skip to content

Aqara Smart LED Strip Driver - #3295

Open
haedo-doo wants to merge 6 commits into
SmartThingsCommunity:mainfrom
haedo-doo:Aqara-SmartLEDStripDriver
Open

haedo-doo wants to merge 6 commits into
SmartThingsCommunity:mainfrom
haedo-doo:Aqara-SmartLEDStripDriver

Conversation

@haedo-doo

@haedo-doo haedo-doo commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Check all that apply

Type of Change

  • WWST Certification Request
    • If this is your first time contributing code:
      • I have reviewed the README.md file
      • I have reviewed the CODE_OF_CONDUCT.md file
      • I have signed the CLA
    • I plan on entering a WWST Certification Request or have entered a request through the WWST Certification console at developer.smartthings.com
  • Bug fix
  • New feature
  • Refactor

Checklist

  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have verified my changes by testing with a device or have communicated a plan for testing
  • I am adding new behavior, such as adding a sub-driver, and have added and run new unit tests to cover the new behavior

Description of Change

Summary of Completed Tests

@github-actions

Copy link
Copy Markdown

Profile category check: ✅ Passed - all profiles have a category defined.

@github-actions

Copy link
Copy Markdown

Duplicate profile check: Passed - no duplicate profiles detected.

@github-actions

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

zigbee-switch_coverage.xml

File Coverage
All files 95% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/inovelli/init.lua 96% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/frient/init.lua 94% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/zll-polling/init.lua 81% ❌
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/inovelli/vzm32-sn/init.lua 95% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/aqara/init.lua 95% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/aqara-strip/init.lua 91% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/color_temp_range_handlers/init.lua 71% ❌
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/init.lua 92% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/preferences.lua 97% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/lazy_load_subdriver.lua 57% ❌
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/frient-IO/unbind_request.lua 71% ❌
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/frient-IO/init.lua 87% ❌
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/inovelli/vzm30-sn/init.lua 95% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/tuya-multi/can_handle.lua 90% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/aqara-light/init.lua 92% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/firstled-io/init.lua 91% ✅
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/aqara/version/init.lua 93% ✅

Minimum allowed coverage is 90%

Generated by 🐒 cobertura-action against 4fb1901

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Test Results

   73 files    551 suites   0s ⏱️
3 375 tests 3 375 ✅ 0 💤 0 ❌
5 423 runs  5 423 ✅ 0 💤 0 ❌

Results for commit 4fb1901.

♻️ This comment has been updated with latest results.

Comment on lines +46 to +51
local function isCCTmode(device)
local lastMode = device:get_latest_state(COMPONENT_MODE, capabilities.mode.ID, capabilities.mode.mode.NAME) or 0
local ret = true
if lastMode == SUPPORTED_MODES[1] then ret = false end
return ret
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

isCCTmode returns true when the mode state is nil. A freshly paired device starts on aqara-lightstrip-rgbw, so until the 0x0509 read-back arrives, commands and reports take the CCT path. Could the fallback be rgbw, which matches the initial profile?

@haedo-doo haedo-doo Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review. The initial profile is rgbw, but the device
remembers its hardware mode across factory reset — so a re-paired
device may actually be in CCT mode until the 0x0509 read-back arrives and the profile is swapped. In that case, defaulting isCCTmode to rgbw would mean commands only reach endpoint 1 and not endpoint 2, leaving lamp2 unresponsive. We'd prefer to keep the current default for this reason.

end

local function set_light_fadeInTimeState(device)
local fadeInTime = device.preferences["stse.lightFadeInTimeInSec"] * 10

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

device.preferences["stse.lightFadeInTimeInSec"] * 10 has no nil guard. The infoChanged profile-change path calls these unconditionally, so a missing preference would raise an error. Please guard it, or fall back to the preference default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Added nil guards to both set_light_fadeInTimeState
and set_light_fadeOutTimeState, and applied the same guard to
set_restore_powerState for consistency.

Comment on lines +341 to +345
device.thread:call_with_delay(MODE_CHANGE_STABILIZE_DELAY_SEC, function()
set_restore_powerState(device)
set_light_fadeInTimeState(device)
set_light_fadeOutTimeState(device)
end)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the profile and a preference change in the same infoChanged, the fade and restore-power writes are sent twice. This is minor, but you could skip the per-preference branches when the profile-change branch has already handled them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The profile-change branch now returns early, so the per-preference branch is skipped when both change in the same event.

Comment on lines +360 to +369
local function do_configure(driver, device)
-- The device auto-reboots (~5-8s) when its operating mode (RGB/CCT) changes.
-- Delay the refresh so we read state after the device has stabilized.
device.thread:call_with_delay(MODE_CHANGE_STABILIZE_DELAY_SEC, function()
do_refresh(driver, device)
end)
device:configure()
device:send(device_management.build_bind_request(device, OnOff.ID, driver.environment_info.hub_zigbee_eui))
do_refresh(driver, device)
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

device:configure() already binds and configures reporting for the attributes the default capabilities register, so the extra build_bind_request for OnOff looks redundant. Also, is endpoint 2 bound and configured for reporting in CCT mode? If it isn’t, lamp2 may not report state changes on its own.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed the redundant OnOff bind. Removed it since device:configure() already binds and configures reporting on the fingerprinted endpoint, and dropped the now-unused device_management import.

On endpoint 2: lamp2 has no independent control path on this product — it only changes in response to hub-issued commands, so hub-side state has remained consistent in our testing. Happy to revisit if you have a specific scenario in mind.

Comment on lines +132 to +135
local hue = (cmd.args.color.hue ~= nil and cmd.args.color.hue > 99) and 99 or cmd.args.color.hue
local sat = cmd.args.color.saturation

local x, y, Y = utils.safe_hsv_to_xy(hue, sat)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If cmd.args.color.hue is nil, for example a setColor carrying only saturation, safe_hsv_to_xy(nil, sat) will error. Please fill the missing value from the latest state, like the hue/sat timer callback does.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. hue and saturation now fall back to the latest known state when the setColor payload omits one of them, consistent with the hue/sat timer callback used by setHue / setSaturation.

@@ -0,0 +1,442 @@
-- Copyright 2025 SmartThings, Inc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Copyright 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -0,0 +1,507 @@
-- Copyright 2025 SmartThings

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Copyright 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Comment on lines +5 to +7
local FINGERPRINTS = {
{ mfr = "LUMI", model = "lumi.dimmer.rcbac1" }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

FINGERPRINTS is rebuilt on every can_handle call. Please move it to module level, or into aqara-strip/fingerprints.lua as other sub-drivers do.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Extracted the fingerprint table into aqara-strip/fingerprints.lua, following the pattern used by the parent aqara/ sub-driver.

Comment on lines +13 to +14
- id: colorTemperature
version: 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The CCT profile limits color temperature to 2700–6500 K, but the RGBW profile has no range config. The driver also never emits colorTemperatureRange, unlike aqara-light. Without it, the app may show a wider range than the device supports. Could you add the same 2700–6500 range, or emit colorTemperatureRange in added?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Added range [2700, 6500] to the colorTemperature capability in aqara-lightstrip-rgbw.yml so it matches the CCT profile.
Both modes share the same color temperature range on this device.

model: lumi.light.cwacn1
deviceProfileName: aqara-light
- id: "LUMI/lumi.dimmer.rcbac1"
deviceLabel: Aqara Smart LED Strip Driver

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Should this just be Aqara Smart LED Strip?

@haedo-doo haedo-doo Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion. "Aqara Smart LED Strip Driver"(lumi.dimmer.rcbac1, retail code ZNDDMK11LM) is a distinct product from the "Aqara LED Strip T1" consumer light strip — the former is a standalone controller module for LED strips. Dropping "Driver" would make the two indistinguishable, so we'd like to keep the current label.

@cbaumler

Copy link
Copy Markdown
Contributor

Hi @haedo-doo, thanks for the updates. The fixes look good.

One more request before we can merge. The STTS results you provided were run on aqara-lightstrip-rgbw. Could you also run STTS on the aqara-lightstrip-cct profile and share a link to the results? Please run it with both LED strips connected (lamp1 and lamp2) so that CCT mode is exercised end to end.

Once we have the CCT STTS results and you've updated the branch and resolved conflicts, we should be able to move forward with merging.

@CLAassistant

CLAassistant commented Nov 4, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

if isCCTmode(device) then curMode = SUPPORTED_MODES[2] end
for _, i in ipairs(COMPONENT_INFO_TABLE) do
if i.supportMode == curMode and i.endPoint == srcEndpoint then
device:emit_component_event(device.profile.components[i.name], evt)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In onoff_handler, an endpoint 1 report is emitted to both main and lamp1, but an endpoint 2 report is emitted only to lamp2. In CCT mode that makes main follow lamp1 and ignore lamp2. As a result:

  • lamp1 off forces main Off even if lamp2 is still On. STTS requires main=On, lamp1=Off, so it can never reach that state.
  • lamp2 on while lamp1 is off leaves main Off even though a strip is lit.

What should main represent in CCT mode? My suggestion is “On if either lamp is On”, which would:

  • keep main consistent with what’s lit,
  • make main=On, lamp1=Off reachable (main on, then lamp1 off leaves lamp2 On), and
    keep main on/off commands going to both endpoints, as they do now.

If you agree, please:

  • emit each endpoint’s report to its own lamp component, then emit main as On if either lamp is On,
  • treat an unknown lamp state as Off,
  • leave RGBW mode unchanged
  • update the unit tests that assert main mirroring lamp1.

Then please rerun STTS on aqara-lightstrip-cct with both strips connected.

If you intend main to track lamp1 only, please tell us why. The STTS precondition can’t be met that way, so we’d need to take it up with the certification team.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @cbaumler, thanks again for the detailed review suggestions — they were very helpful.

The CCT main-state handling has been updated and STTS now passes;
results are attached below. Conflicts with main have also been resolved.

Please let me know if there's anything else you'd like me to address before merging.

STTS result: https://developer.smartthings.com/console/test/sessions/TNW7MHCZ35

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @haedo-doo, the changes look good to me. We can merge this once the certification team has reviewed and approved the STTS results.

do_refresh(driver, device)
end)
device:configure()
do_refresh(driver, device)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this immediate refresh needed as well as one that is delayed 10sec?

Suggested change
do_refresh(driver, device)

@cjswedes cjswedes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is a lot of duplication of functionality in our defaults when in RGBW mode, that I wish we could easily avoid doing, but I dont see an elegant way to do it.

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.

4 participants