diff --git a/drivers/SmartThings/zigbee-switch/fingerprints.yml b/drivers/SmartThings/zigbee-switch/fingerprints.yml index 90862aa61f..53d2701979 100644 --- a/drivers/SmartThings/zigbee-switch/fingerprints.yml +++ b/drivers/SmartThings/zigbee-switch/fingerprints.yml @@ -2451,6 +2451,12 @@ zigbeeManufacturer: manufacturer: JNL model: Y-K002-001 deviceProfileName: basic-switch + #FIRSTLED + - id: "ManDai Technology/DC2DC12MiV1" + deviceLabel: Easy-Fit Driver + manufacturer: ManDai Technology + model: DC2DC12MiV1 + deviceProfileName: light-color-temp-time-restore zigbeeGeneric: - id: "genericSwitch" deviceLabel: Zigbee Switch diff --git a/drivers/SmartThings/zigbee-switch/profiles/light-color-temp-time-restore.yml b/drivers/SmartThings/zigbee-switch/profiles/light-color-temp-time-restore.yml new file mode 100644 index 0000000000..c4a0ee1151 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/profiles/light-color-temp-time-restore.yml @@ -0,0 +1,70 @@ +name: light-color-temp-time-restore +components: + - id: main + capabilities: + - id: switch + version: 1 + - id: switchLevel + version: 1 + config: + values: + - key: "level.value" + range: [1, 100] + - id: colorTemperature + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: Light +preferences: + - title: "上电状态(power-on state/파워온 상태)" + name: powerOnMode + description: "上电状态(power-on state/파워온 상태)" + required: false + preferenceType: enumeration + definition: + options: + 0: "关闭(off/닫다)" + 1: "打开(on/열다)" + 2: "恢复记忆状态(restore/복원)" + default: 0 + - title: "驱动电流(drive current/구동 전류)" + name: ledDriveCurrent + description: "驱动电流(drive current/구동 전류)/50~333mA" + required: false + preferenceType: integer + definition: + minimum: 50 + maximum: 333 + default: 270 + - title: "亮度时间(dimmingTransitionTime/밝기 전환 시간)" + name: dimTransitionTime + description: "亮度过渡时间(dimming transition time/밝기 전환 시간)1000~10000ms" + required: false + preferenceType: integer + definition: + minimum: 1000 + maximum: 10000 + default: 1000 + - title: "色温时间(CCTTransitionTime/색온 전환 시간)" + name: colorTempTransitionTime + description: "色温过渡时间(CCT transition time/색온 전환 시간)1000~10000ms" + required: false + preferenceType: integer + definition: + minimum: 1000 + maximum: 10000 + default: 1000 + - title: "输出模式(output mode/출력 모드)" + name: outputMode + description: "输出模式(output mode/출력 모드)" + required: false + preferenceType: enumeration + definition: + options: + 0: "双路CW(dual CW/듀얼 CW)" + 1: "双路WC(dual WC/듀얼 WC)" + 2: "单路(single channel/단일 채널)" + default: 0 diff --git a/drivers/SmartThings/zigbee-switch/src/firstled-light/can_handle.lua b/drivers/SmartThings/zigbee-switch/src/firstled-light/can_handle.lua new file mode 100644 index 0000000000..6b02320ebf --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/firstled-light/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 "firstled-light.fingerprints" + for _, fingerprint in ipairs(FINGERPRINTS) do + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + local subdriver = require("firstled-light") + return true, subdriver + end + end + return false +end diff --git a/drivers/SmartThings/zigbee-switch/src/firstled-light/fingerprints.lua b/drivers/SmartThings/zigbee-switch/src/firstled-light/fingerprints.lua new file mode 100644 index 0000000000..ba8c2347b3 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/firstled-light/fingerprints.lua @@ -0,0 +1,6 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +return { + { mfr = "ManDai Technology", model = "DC2DC12MiV1" } +} diff --git a/drivers/SmartThings/zigbee-switch/src/firstled-light/init.lua b/drivers/SmartThings/zigbee-switch/src/firstled-light/init.lua new file mode 100644 index 0000000000..ab81b0f0cf --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/firstled-light/init.lua @@ -0,0 +1,63 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" + +local PRIVATE_CLUSTER_ID = 0xFC00 + +local preference_map = { + ["outputMode"] = { + cluster_id = PRIVATE_CLUSTER_ID, + attribute_id = 0x0000, + data_type = data_types.Uint8 + }, + ["powerOnMode"] = { + cluster_id = PRIVATE_CLUSTER_ID, + attribute_id = 0x0001, + data_type = data_types.Uint8 + }, + ["ledDriveCurrent"] = { + cluster_id = PRIVATE_CLUSTER_ID, + attribute_id = 0x0002, + data_type = data_types.Uint16 + }, + ["dimTransitionTime"] = { + cluster_id = PRIVATE_CLUSTER_ID, + attribute_id = 0x0003, + data_type = data_types.Uint16 + }, + ["colorTempTransitionTime"] = { + cluster_id = PRIVATE_CLUSTER_ID, + attribute_id = 0x0004, + data_type = data_types.Uint16 + } +} + +local function device_info_changed(driver, device, event, args) + local preferences = device.preferences + local old_preferences = args.old_st_store.preferences + if preferences ~= nil then + for id, attr in pairs(preference_map) do + local old_value = old_preferences[id] + local value = preferences[id] + if value ~= nil and value ~= old_value then + value = tonumber(value) + device:send(cluster_base.write_attribute(device, + data_types.ClusterId(attr.cluster_id), + data_types.AttributeId(attr.attribute_id), + data_types.validate_or_build_type(value, attr.data_type, "payload"))) + end + end + end +end + +local firstled_light_handlers = { + NAME = "firstled-light handlers", + lifecycle_handlers = { + infoChanged = device_info_changed + }, + can_handle = require("firstled-light.can_handle") +} + +return firstled_light_handlers diff --git a/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua b/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua index 69be094da4..45adca4ed5 100644 --- a/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua +++ b/drivers/SmartThings/zigbee-switch/src/sub_drivers.lua @@ -36,5 +36,6 @@ return { lazy_load_if_possible("frient"), lazy_load_if_possible("frient-IO"), lazy_load_if_possible("color_temp_range_handlers"), - lazy_load_if_possible("stateless_handlers") + lazy_load_if_possible("stateless_handlers"), + lazy_load_if_possible("firstled-light") } diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_firstled_light.lua b/drivers/SmartThings/zigbee-switch/src/test/test_firstled_light.lua new file mode 100644 index 0000000000..79b77ab12a --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/test/test_firstled_light.lua @@ -0,0 +1,106 @@ +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local test = require "integration_test" +local t_utils = require "integration_test.utils" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" + +local PRIVATE_CLUSTER_ID = 0xFC00 + +local mock_device = test.mock_device.build_test_zigbee_device( + { + profile = t_utils.get_profile_definition("light-color-temp-time-restore.yml"), + fingerprinted_endpoint_id = 0x01, + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "ManDai Technology", + model = "DC2DC12MiV1", + server_clusters = { 0x0006, 0x0008, 0x0300 } + } + } + } +) + +zigbee_test_utils.prepare_zigbee_env_info() + +local function test_init() + test.mock_device.add_test_device(mock_device) +end + +test.set_test_init_function(test_init) + +-- ====================== Preferences ====================== +test.register_coroutine_test("infoChanged - outputMode 0", function() + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ preferences = { outputMode = "0" }})) + test.socket.zigbee:__expect_send({ mock_device.id, + cluster_base.write_attribute(mock_device, + data_types.ClusterId(PRIVATE_CLUSTER_ID), + data_types.AttributeId(0x0000), + data_types.validate_or_build_type(0, data_types.Uint8, "payload")) + }) + end, + { + min_api_version = 19 + } +) + +test.register_coroutine_test("infoChanged - powerOnMode 0", function() + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ preferences = { powerOnMode = "0" }})) + test.socket.zigbee:__expect_send({ mock_device.id, + cluster_base.write_attribute(mock_device, + data_types.ClusterId(PRIVATE_CLUSTER_ID), + data_types.AttributeId(0x0001), + data_types.validate_or_build_type(0, data_types.Uint8, "payload")) + }) + end, + { + min_api_version = 19 + } +) + +test.register_coroutine_test("infoChanged - ledDriveCurrent 100", function() + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ preferences = { ledDriveCurrent = "100" }})) + test.socket.zigbee:__expect_send({ mock_device.id, + cluster_base.write_attribute(mock_device, + data_types.ClusterId(PRIVATE_CLUSTER_ID), + data_types.AttributeId(0x0002), + data_types.validate_or_build_type(100, data_types.Uint16, "payload")) + }) + end, + { + min_api_version = 19 + } +) + +test.register_coroutine_test("infoChanged - dimTransitionTime 2000", function() + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ preferences = { dimTransitionTime = "2000" }})) + test.socket.zigbee:__expect_send({ mock_device.id, + cluster_base.write_attribute(mock_device, + data_types.ClusterId(PRIVATE_CLUSTER_ID), + data_types.AttributeId(0x0003), + data_types.validate_or_build_type(2000, data_types.Uint16, "payload")) + }) + end, + { + min_api_version = 19 + } +) + +test.register_coroutine_test("infoChanged - colorTempTransitionTime 2000", function() + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ preferences = { colorTempTransitionTime = "2000" }})) + test.socket.zigbee:__expect_send({ mock_device.id, + cluster_base.write_attribute(mock_device, + data_types.ClusterId(PRIVATE_CLUSTER_ID), + data_types.AttributeId(0x0004), + data_types.validate_or_build_type(2000, data_types.Uint16, "payload")) + }) + end, + { + min_api_version = 19 + } +) + +test.run_registered_tests()