diff --git a/drivers/SmartThings/zigbee-switch/fingerprints.yml b/drivers/SmartThings/zigbee-switch/fingerprints.yml index 8765be8aeb..7d561caeba 100644 --- a/drivers/SmartThings/zigbee-switch/fingerprints.yml +++ b/drivers/SmartThings/zigbee-switch/fingerprints.yml @@ -1730,6 +1730,16 @@ zigbeeManufacturer: manufacturer: LEDVANCE model: RT TW deviceProfileName: color-temp-bulb + - id: "LEDVANCE/PLUG COMPACT EU EM T" + deviceLabel: SMART ZIGBEE COMPACT OUTDOOR PLUG EU + manufacturer: LEDVANCE + model: PLUG COMPACT EU EM T + deviceProfileName: switch-power-energy + - id: "LEDVANCE/PLUG EU EM T" + deviceLabel: SMART ZIGBEE PLUG EU EM T + manufacturer: LEDVANCE + model: PLUG EU EM T + deviceProfileName: switch-power-energy - id: "OSRAM/LIGHTIFY Edge-lit flushmount" deviceLabel: SYLVANIA Light manufacturer: OSRAM diff --git a/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/can_handle.lua b/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/can_handle.lua new file mode 100644 index 0000000000..2d7f42bac8 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/can_handle.lua @@ -0,0 +1,13 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +return function(opts, driver, device, ...) + local FINGERPRINTS = require("ledvance-metering-plug.fingerprints") + for _, fingerprint in ipairs(FINGERPRINTS) do + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + local subdriver = require("ledvance-metering-plug") + return true, subdriver + end + end + return false +end diff --git a/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/fingerprints.lua b/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/fingerprints.lua new file mode 100644 index 0000000000..50542e69ea --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/fingerprints.lua @@ -0,0 +1,7 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +return { + { mfr = "LEDVANCE", model = "PLUG COMPACT EU EM T" }, + { mfr = "LEDVANCE", model = "PLUG EU EM T" } +} diff --git a/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/init.lua b/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/init.lua new file mode 100644 index 0000000000..e907f25a28 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/ledvance-metering-plug/init.lua @@ -0,0 +1,23 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local zigbee_constants = require "st.zigbee.constants" + +local function device_init(driver, device) + if device:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == nil then + device:set_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY, 1, {persist = true}) + end + if device:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == nil then + device:set_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY, 100, {persist = true}) + end +end + +local ledvance_metering_plug = { + NAME = "LEDVANCE Metering Plug", + lifecycle_handlers = { + init = device_init + }, + can_handle = require("ledvance-metering-plug.can_handle") +} + +return ledvance_metering_plug diff --git a/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua b/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua index 69be094da4..736c2a0464 100644 --- a/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua +++ b/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua @@ -14,6 +14,7 @@ return { lazy_load_if_possible("sinope"), lazy_load_if_possible("sinope-dimmer"), lazy_load_if_possible("zigbee-dimmer-power-energy"), + lazy_load_if_possible("ledvance-metering-plug"), lazy_load_if_possible("zigbee-metering-plug-power-consumption-report"), lazy_load_if_possible("jasco"), lazy_load_if_possible("multi-switch-no-master"), diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_ledvance_metering_plug.lua b/drivers/SmartThings/zigbee-switch/src/test/test_ledvance_metering_plug.lua new file mode 100644 index 0000000000..34ab7702e8 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/test/test_ledvance_metering_plug.lua @@ -0,0 +1,94 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local test = require "integration_test" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" +local t_utils = require "integration_test.utils" +local zigbee_constants = require "st.zigbee.constants" + +local mock_device = test.mock_device.build_test_zigbee_device( + { + profile = t_utils.get_profile_definition("switch-power-energy.yml"), + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "LEDVANCE", + model = "PLUG COMPACT EU EM T", + server_clusters = { 0x0006, 0x0702 } + } + } + } +) + +local mock_device_eu_em_t = test.mock_device.build_test_zigbee_device( + { + profile = t_utils.get_profile_definition("switch-power-energy.yml"), + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "LEDVANCE", + model = "PLUG EU EM T", + server_clusters = { 0x0006, 0x0702 } + } + } + } +) + +zigbee_test_utils.prepare_zigbee_env_info() + +local function test_init() + test.mock_device.add_test_device(mock_device) + test.mock_device.add_test_device(mock_device_eu_em_t) +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "Device init should set default multiplier and divisor only when not already set", + function() + assert(mock_device:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == nil) + assert(mock_device:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == nil) + test.socket.device_lifecycle:__queue_receive({ mock_device.id, "init" }) + test.wait_for_events() + assert(mock_device:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == 1) + assert(mock_device:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == 100) + end +) + +test.register_coroutine_test( + "Device init should preserve device-reported multiplier and divisor", + function() + mock_device:set_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY, 5, {persist = true}) + mock_device:set_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY, 1000, {persist = true}) + test.socket.device_lifecycle:__queue_receive({ mock_device.id, "init" }) + test.wait_for_events() + assert(mock_device:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == 5) + assert(mock_device:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == 1000) + end +) + +test.register_coroutine_test( + "Device init should set default multiplier and divisor only when not already set - PLUG EU EM T", + function() + assert(mock_device_eu_em_t:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == nil) + assert(mock_device_eu_em_t:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == nil) + test.socket.device_lifecycle:__queue_receive({ mock_device_eu_em_t.id, "init" }) + test.wait_for_events() + assert(mock_device_eu_em_t:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == 1) + assert(mock_device_eu_em_t:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == 100) + end +) + +test.register_coroutine_test( + "Device init should preserve device-reported multiplier and divisor - PLUG EU EM T", + function() + mock_device_eu_em_t:set_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY, 5, {persist = true}) + mock_device_eu_em_t:set_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY, 1000, {persist = true}) + test.socket.device_lifecycle:__queue_receive({ mock_device_eu_em_t.id, "init" }) + test.wait_for_events() + assert(mock_device_eu_em_t:get_field(zigbee_constants.SIMPLE_METERING_MULTIPLIER_KEY) == 5) + assert(mock_device_eu_em_t:get_field(zigbee_constants.SIMPLE_METERING_DIVISOR_KEY) == 1000) + end +) + +test.run_registered_tests()