diff --git a/drivers/SmartThings/zwave-switch/fingerprints.yml b/drivers/SmartThings/zwave-switch/fingerprints.yml index d90a0c6b76..8320d8901f 100644 --- a/drivers/SmartThings/zwave-switch/fingerprints.yml +++ b/drivers/SmartThings/zwave-switch/fingerprints.yml @@ -916,6 +916,12 @@ zwaveManufacturer: manufacturerId: 0x010F productType: 0x0102 deviceProfileName: fibaro-dimmer-2 + - id: Philio/PAD19 + deviceLabel: PAD19 + manufacturerId: 0x013C + productType: 0x0005 + productId: 0x008A + deviceProfileName: philio-dimmer-switch #Shelly/Qubino - id: 1120/2/137 deviceLabel: Wave Plug UK diff --git a/drivers/SmartThings/zwave-switch/profiles/philio-dimmer-switch.yml b/drivers/SmartThings/zwave-switch/profiles/philio-dimmer-switch.yml new file mode 100644 index 0000000000..ccf2d69111 --- /dev/null +++ b/drivers/SmartThings/zwave-switch/profiles/philio-dimmer-switch.yml @@ -0,0 +1,16 @@ +name: philio-dimmer-switch +components: +- id: main + capabilities: + - id: switch + version: 1 + - id: switchLevel + version: 1 + config: + values: + - key: "level.value" + range: [0, 99] + - id: refresh + version: 1 + categories: + - name: Switch diff --git a/drivers/SmartThings/zwave-switch/src/init.lua b/drivers/SmartThings/zwave-switch/src/init.lua index 405600e962..26cca570a7 100644 --- a/drivers/SmartThings/zwave-switch/src/init.lua +++ b/drivers/SmartThings/zwave-switch/src/init.lua @@ -17,8 +17,6 @@ local SwitchMultilevel = (require "st.zwave.CommandClass.SwitchMultilevel")({ ve local preferencesMap = require "preferences" local configurationsMap = require "configurations" -local lazy_load_if_possible = require "lazy_load_subdriver" - --- Map component to end_points(channels) --- --- @param device st.zwave.Device @@ -120,28 +118,7 @@ local driver_template = { [SwitchMultilevel.STOP_LEVEL_CHANGE] = switch_multilevel_stop_level_change_handler } }, - sub_drivers = { - lazy_load_if_possible("eaton-accessory-dimmer"), - lazy_load_if_possible("inovelli"), - lazy_load_if_possible("dawon-smart-plug"), - lazy_load_if_possible("inovelli-2-channel-smart-plug"), - lazy_load_if_possible("zwave-dual-switch"), - lazy_load_if_possible("eaton-anyplace-switch"), - lazy_load_if_possible("fibaro-wall-plug-us"), - lazy_load_if_possible("dawon-wall-smart-switch"), - lazy_load_if_possible("zooz-power-strip"), - lazy_load_if_possible("aeon-smart-strip"), - lazy_load_if_possible("qubino-switches"), - lazy_load_if_possible("fibaro-double-switch"), - lazy_load_if_possible("fibaro-single-switch"), - lazy_load_if_possible("eaton-5-scene-keypad"), - lazy_load_if_possible("ecolink-switch"), - lazy_load_if_possible("multi-metering-switch"), - lazy_load_if_possible("zooz-zen-30-dimmer-relay"), - lazy_load_if_possible("multichannel-device"), - lazy_load_if_possible("aeotec-smart-switch"), - lazy_load_if_possible("aeotec-heavy-duty") - }, + sub_drivers = require("sub_drivers"), lifecycle_handlers = { init = device_init, infoChanged = info_changed, diff --git a/drivers/SmartThings/zwave-switch/src/philio-dimmer-switch/can_handle.lua b/drivers/SmartThings/zwave-switch/src/philio-dimmer-switch/can_handle.lua new file mode 100644 index 0000000000..0fd1886f52 --- /dev/null +++ b/drivers/SmartThings/zwave-switch/src/philio-dimmer-switch/can_handle.lua @@ -0,0 +1,20 @@ +-- can_handle.lua +-- 判斷是否為 Philio PAD19 裝置 + +local subdriver = require("philio-dimmer-switch") + +local function can_handle_pad19(opts, driver, device, ...) + local fingerprint_list = { + {mfr = 0x013C, prod_type = 0x0005, prod_id = 0x008A}, -- Philio PAD19 + } + + for _, fingerprint in ipairs(fingerprint_list) do + if device:id_match(fingerprint.mfr, fingerprint.prod_type, fingerprint.prod_id) then + return true, subdriver + end + end + + return false +end + +return can_handle_pad19 diff --git a/drivers/SmartThings/zwave-switch/src/philio-dimmer-switch/init.lua b/drivers/SmartThings/zwave-switch/src/philio-dimmer-switch/init.lua new file mode 100644 index 0000000000..e0599b7c0c --- /dev/null +++ b/drivers/SmartThings/zwave-switch/src/philio-dimmer-switch/init.lua @@ -0,0 +1,154 @@ +local capabilities = require "st.capabilities" +--- @type st.zwave.Driver +local ZwaveDriver = require "st.zwave.driver" +--- @type st.zwave.CommandClass +local cc = require "st.zwave.CommandClass" +--- @type st.utils +local utils = require "st.utils" +--- @type st.zwave.constants +local constants = require "st.zwave.constants" +--- @type st.zwave.CommandClass.Basic +local Basic = (require "st.zwave.CommandClass.Basic")({ version = 1 }) +--- @type st.zwave.CommandClass.SwitchMultilevel +local SwitchMultilevel = (require "st.zwave.CommandClass.SwitchMultilevel")({ version = 4 }) + + +-- print("DEBUG: philio-dimmer-switch/init.lua loaded") + +local function dimmer_event(driver, device, cmd) + local raw = cmd.args.value or cmd.args.target_value or 0 + + if raw == "OFF_DISABLE" then + raw = 0 + end + + if type(raw) ~= "number" then + raw = 0 + end + + local level = utils.clamp_value(raw, 0, 99) + + device:emit_event(level > 0 and capabilities.switch.switch.on() or capabilities.switch.switch.off()) + device:emit_event(capabilities.switchLevel.level(level)) +end + +local function basic_report_handler(driver, device, cmd) + local basic_level = cmd.args.value or 0 + local level = utils.clamp_value(basic_level, 0, 99) + + device:emit_event(level > 0 and capabilities.switch.switch.on() or capabilities.switch.switch.off()) + device:emit_event(capabilities.switchLevel.level(level)) +end + +local function switch_on_handler(driver, device) + device:send(Basic:Set({value = 0xff})) + device.thread:call_with_delay(4, function(d) + device:send(SwitchMultilevel:Get({})) + end) +end + +local function switch_off_handler(driver, device) + device:send(Basic:Set({value = 0x00})) + device.thread:call_with_delay(4, function(d) + device:send(SwitchMultilevel:Get({})) + end) +end + +local function switch_level_set(driver, device, cmd) + local level = utils.round(cmd.args.level) + level = utils.clamp_value(level, 0, 99) + + device:emit_event(level > 0 and capabilities.switch.switch.on() or capabilities.switch.switch.off()) + device:emit_event(capabilities.switchLevel.level(level)) + + ------------------------------------------------------------------ + -- 修正:SmartThings 可能送出 rate="default",不是數字 → 會造成崩潰 + ------------------------------------------------------------------ + local raw_rate = cmd.args.rate + local dimmingDuration = tonumber(raw_rate) -- dimming duration in seconds + if dimmingDuration == nil then + dimmingDuration = 0 -- Z-Wave duration=0 = 快速/立即 + end + + device:send(SwitchMultilevel:Set({ value=level, duration=dimmingDuration })) + local function query_level() + device:send(SwitchMultilevel:Get({})) + end + -- delay shall be at least 5 sec. + local delay = math.max(dimmingDuration + constants.DEFAULT_POST_DIMMING_DELAY , constants.MIN_DIMMING_GET_STATUS_DELAY) --delay in seconds + device.thread:call_with_delay(delay, query_level) +end + +---- Refresh 指令函式(SmartThings Test Suite 必要) +local function refresh_cmd(driver, device, command) + -- print("DEBUG: PAD19 refresh_cmd called") + + -- 取得目前開關狀態 + local switch_get = Basic:Get({}) + device:send(switch_get) + + -- 取得目前dimmer的level + local switchlevel_get = SwitchMultilevel:Get({}) + device:send(switchlevel_get) +end + +------------------------------------------------------------------- +-- Lifecycle +------------------------------------------------------------------- +local function device_init(driver, device) + -- print("DEBUG: PAD19 device_init called") +end + +local function device_added(driver, device) + -- print("DEBUG: PAD19 device_added - init state off") + device:emit_event(capabilities.switch.switch.off()) + device:emit_event(capabilities.switchLevel.level(0)) + -- print("DEBUG: PAD19 Initial switchlevel = 0") +end + +-- NEW: 修正 driverSwitched 崩潰 +local function device_driver_switched(driver, device, event, args) + -- print("DEBUG: PAD19 driverSwitched - ignored") +end + +local pad19_driver_template = { + NAME = "Philio PAD19 Dimmer Switch", + zwave_handlers = { + [cc.BASIC] = { + [Basic.SET] = dimmer_event, + [Basic.REPORT] = basic_report_handler + }, + [cc.SWITCH_MULTILEVEL] = { + [SwitchMultilevel.SET] = dimmer_event, + [SwitchMultilevel.REPORT] = dimmer_event + } + }, + capability_handlers = { + [capabilities.switch.ID] = { + [capabilities.switch.commands.on.NAME] = switch_on_handler, + [capabilities.switch.commands.off.NAME] = switch_off_handler + }, + [capabilities.switchLevel.ID] = { + [capabilities.switchLevel.commands.setLevel.NAME] = switch_level_set + }, + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = refresh_cmd + } + }, + + lifecycle_handlers = { + init = device_init, + added = device_added, + driverSwitched = device_driver_switched + }, + + -- 設置 Z-Wave 設備配置 + zwave_config = {} +-- zwave_config = {}, + + -- 指定can_handle腳本, 讓上層可以先檢查這台Device是否能用這個子驅動控制,可以才載入 +-- can_handle = require("philio-dimmer-switch.can_handle") +} + +-- 回傳驅動範本 +return pad19_driver_template diff --git a/drivers/SmartThings/zwave-switch/src/sub_drivers.lua b/drivers/SmartThings/zwave-switch/src/sub_drivers.lua new file mode 100644 index 0000000000..9df5ebfb51 --- /dev/null +++ b/drivers/SmartThings/zwave-switch/src/sub_drivers.lua @@ -0,0 +1,29 @@ + +-- Copyright 2026 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local lazy_load_if_possible = require "lazy_load_subdriver" + +return { + lazy_load_if_possible("eaton-accessory-dimmer"), + lazy_load_if_possible("inovelli"), + lazy_load_if_possible("dawon-smart-plug"), + lazy_load_if_possible("inovelli-2-channel-smart-plug"), + lazy_load_if_possible("zwave-dual-switch"), + lazy_load_if_possible("eaton-anyplace-switch"), + lazy_load_if_possible("fibaro-wall-plug-us"), + lazy_load_if_possible("dawon-wall-smart-switch"), + lazy_load_if_possible("zooz-power-strip"), + lazy_load_if_possible("aeon-smart-strip"), + lazy_load_if_possible("qubino-switches"), + lazy_load_if_possible("fibaro-double-switch"), + lazy_load_if_possible("fibaro-single-switch"), + lazy_load_if_possible("eaton-5-scene-keypad"), + lazy_load_if_possible("ecolink-switch"), + lazy_load_if_possible("multi-metering-switch"), + lazy_load_if_possible("zooz-zen-30-dimmer-relay"), + lazy_load_if_possible("multichannel-device"), + lazy_load_if_possible("aeotec-smart-switch"), + lazy_load_if_possible("aeotec-heavy-duty"), + lazy_load_if_possible("philio-dimmer-switch") +}