Skip to content

WWSTCERT-10189 Ledvance zigbee meter plug#2729

Open
LQ107 wants to merge 18 commits intoSmartThingsCommunity:mainfrom
LQ107:ledvance_zigbee_meter_plug
Open

WWSTCERT-10189 Ledvance zigbee meter plug#2729
LQ107 wants to merge 18 commits intoSmartThingsCommunity:mainfrom
LQ107:ledvance_zigbee_meter_plug

Conversation

@LQ107
Copy link

@LQ107 LQ107 commented Jan 26, 2026

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

@greens greens changed the title Ledvance zigbee meter plug WWSTCERT-10189 Ledvance zigbee meter plug Feb 4, 2026
Comment on lines +8 to +13
fingerprints:
- manufacturer: "LEDVANCE"
model: "PLUG COMPACT EU EM T"
deviceProfileName: "switch-power-energy"
id: "LEDVANCE/PLUG COMPACT EU EM T"
deviceLabel: "SMART ZIGBEE COMPACT OUTDOOR PLUG EU" No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

please remove the changes to this file

local can_handle_simple_metering_config = function(opts, driver, device)
-- 检查设备是否支持 Simple Metering 集群 (0x0702)
for _, cluster in ipairs(device.server_clusters) do
if cluster == 0x0702 then
Copy link
Contributor

Choose a reason for hiding this comment

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

we have other devices for which this would return true where we would not want your specific device's logic to be used, which is why we usually gate subdrivers via fingerprints

Comment on lines +10 to +35
local function device_init(driver, device)
-- 在设备初始化时设置 multipliers 和 divisors
device:configure()

-- 设置 Multiplier 为 1
local write_multiplier_cmd = SimpleMetering.server.commands.WriteAttributes(device)
if write_multiplier_cmd then
device:send_to_component(
write_multiplier_cmd({
{id = SimpleMetering.attributes.Multiplier.ID, value = 1, DataType = 0x22} -- 0x22 is 24-bit integer
}),
"main"
)
end

-- 设置 Divisor 为 100
local write_divisor_cmd = SimpleMetering.server.commands.WriteAttributes(device)
if write_divisor_cmd then
device:send_to_component(
write_divisor_cmd({
{id = SimpleMetering.attributes.Divisor.ID, value = 100, DataType = 0x23} -- 0x23 is 32-bit integer
}),
"main"
)
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

It is often the case that the driver init event happens before the radio is up after a hub restart, resulting in zigbee messages failing to be sent. I would recommend moving these to added or configure, since they likely do not need to be re-sent to the device every time they hub restarts.

Comment on lines +40 to +41
local divisor = device:get_field(SimpleMetering.attributes.Divisor.ID) or 100
local multiplier = device:get_field(SimpleMetering.attributes.Multiplier.ID) or 1
Copy link
Contributor

Choose a reason for hiding this comment

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

this field is not likely to be set. We do set a similar field from "st.zigbee.constants": constants.SIMPLE_METERING_MULTIPLIER_KEY (and divisor) in our defaults, but we never use the attribute ID as a key,

if write_multiplier_cmd then
device:send_to_component(
write_multiplier_cmd({
{id = SimpleMetering.attributes.Multiplier.ID, value = 1, DataType = 0x22} -- 0x22 is 24-bit integer
Copy link
Contributor

@greens greens Feb 4, 2026

Choose a reason for hiding this comment

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

we provide "st.zigbee.data_types" which has a Int24 (and Int32 constant)

if write_divisor_cmd then
device:send_to_component(
write_divisor_cmd({
{id = SimpleMetering.attributes.Divisor.ID, value = 100, DataType = 0x23} -- 0x23 is 32-bit integer
Copy link
Contributor

Choose a reason for hiding this comment

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

in the ZCL this attribute is an Int24

Copy link
Contributor

@greens greens left a comment

Choose a reason for hiding this comment

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

It seems fairly obvious that your AI agent was not up to the task here.

@LQ107
Copy link
Author

LQ107 commented Feb 11, 2026 via email

Comment on lines +8 to +28
local function energy_meter_handler(driver, device, value, zb_rx)
local raw_value = value.value

if type(raw_value) ~= "number" or raw_value < 0 then
return
end

local divisor = device:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) or 100
local multiplier = device:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) or 1

if divisor == 0 then
return
end

local calculated_value = (raw_value * multiplier) / divisor

device:emit_event_for_endpoint(
zb_rx.address_header.src_endpoint.value,
capabilities.energyMeter.energy({ value = calculated_value, unit = "kWh" })
)
end
Copy link
Contributor

Choose a reason for hiding this comment

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

our default handlers should handle this case, provided your device properly reports its simple metering multiplier and divisor.

Copy link
Contributor

Choose a reason for hiding this comment

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

I still think this function can be removed.

@@ -0,0 +1,44 @@
-- Copyright 2025 SmartThings, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

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

date

Copy link
Contributor

@greens greens left a comment

Choose a reason for hiding this comment

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

Based on what you have here, I think all you need to add is the entry in fingerprints.yml. There does not seem to be any device-specific behavior that is different from our default handlers.

@LQ107
Copy link
Author

LQ107 commented Feb 26, 2026 via email

@greens
Copy link
Contributor

greens commented Feb 27, 2026

@LQ107 we should be reading the multiplier and divisor from the device on join. Is that not happening? Is it inaccurate?

@greens
Copy link
Contributor

greens commented Feb 27, 2026

If not, you can simply set the multiplier and divisor fields in your driver's init handler rather than overwriting the default zigbee message handling

either way, we would like to see some unit tests added to verify your code's behavior

@LQ107
Copy link
Author

LQ107 commented Mar 3, 2026 via email

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Test Results

   72 files    488 suites   0s ⏱️
2 658 tests 2 658 ✅ 0 💤 0 ❌
4 506 runs  4 506 ✅ 0 💤 0 ❌

Results for commit 99ce6ea.

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

File Coverage
All files 96%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/laisiao/init.lua 96%
/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/init.lua 92%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/aqara/init.lua 94%
/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/inovelli/init.lua 98%
/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/aqara-light/init.lua 92%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/configurations/init.lua 98%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/frient/init.lua 94%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/aqara/version/init.lua 94%
/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/frient-IO/unbind_request.lua 71%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-switch/src/frient-IO/init.lua 88%

Minimum allowed coverage is 90%

Generated by 🐒 cobertura-action against 99ce6ea

Comment on lines +4 to +6
local can_handle_simple_metering_config = function(opts, driver, device)
return device.fingerprinted == true
end
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not how you should be checking for a specific device model. This will return true for every device, I expect.

Copy link
Contributor

Choose a reason for hiding this comment

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

You should only need the init functions, or just rely on the defaults to read the multiplier/divisor.

Please include some unit tests if you think your changes are necessary.

@LQ107
Copy link
Author

LQ107 commented Mar 4, 2026 via email

@LQ107 LQ107 requested a review from greens March 4, 2026 06:29
Copy link
Contributor

Choose a reason for hiding this comment

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

remove

Copy link
Author

Choose a reason for hiding this comment

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

I had removed

Copy link
Contributor

Choose a reason for hiding this comment

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

This is not how we handle unit tests. See the /tests directory for examples.

Copy link
Author

Choose a reason for hiding this comment

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

yes ,I had remove this file

Copy link
Contributor

Choose a reason for hiding this comment

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

revert these changes

just use git rm to remove files from tracking

Comment on lines +51 to +55
[SimpleMetering.attributes.Multiplier.ID] = function(driver, device, value, zb_rx)
device:set_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY, value.value, {persist = true})
end,
[SimpleMetering.attributes.Divisor.ID] = function(driver, device, value, zb_rx)
device:set_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY, value.value, {persist = true})
Copy link
Contributor

Choose a reason for hiding this comment

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

this is already how we handle these attributes by default

this code is redundant

},
lifecycle_handlers = {
init = function(driver, device)
configurations.power_reconfig_wrapper(device_init)(driver, device)
Copy link
Contributor

Choose a reason for hiding this comment

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

power_reconfig_wrapper has recently been renamed. this code will fail

Copy link
Contributor

@greens greens left a comment

Choose a reason for hiding this comment

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

Change the copyright date to be 2026.

Include some unit tests.

Again, to reiterate for the third time, I do not think any of these changes should be necessary. We read the multiplier and divisor from the device during join and use them for energy calculations.

If that is not your experience when testing, then the only change you need is the init handler to set those fields manually.

@LQ107 LQ107 requested a review from greens March 10, 2026 06:52
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.

2 participants