From 2f5426e676ad909221171aa626a30f58612d0565 Mon Sep 17 00:00:00 2001 From: jtp10181 Date: Mon, 29 Sep 2025 18:41:18 -0500 Subject: [PATCH 1/2] Add Zooz ZSE50 to zwave-siren --- .../SmartThings/zwave-siren/fingerprints.yml | 8 +- .../zwave-siren/profiles/zooz-zse50.yml | 309 +++++++++++++ drivers/SmartThings/zwave-siren/src/init.lua | 6 +- .../zwave-siren/src/preferences.lua | 28 +- .../zwave-siren/src/zooz-zse50/init.lua | 409 ++++++++++++++++++ 5 files changed, 757 insertions(+), 3 deletions(-) create mode 100644 drivers/SmartThings/zwave-siren/profiles/zooz-zse50.yml create mode 100644 drivers/SmartThings/zwave-siren/src/zooz-zse50/init.lua diff --git a/drivers/SmartThings/zwave-siren/fingerprints.yml b/drivers/SmartThings/zwave-siren/fingerprints.yml index 1e36e4af81..c4f6fdedea 100644 --- a/drivers/SmartThings/zwave-siren/fingerprints.yml +++ b/drivers/SmartThings/zwave-siren/fingerprints.yml @@ -1,10 +1,16 @@ zwaveManufacturer: - - id: "Zooz" + - id: "Zooz/ZSE19" deviceLabel: Zooz Multisiren manufacturerId: 0x027A productType: 0x000C productId: 0x0003 deviceProfileName: multifunctional-siren + - id: "Zooz/ZSE50" + deviceLabel: Zooz ZSE50 Siren & Chime + manufacturerId: 0x027A + productType: 0x0004 + productId: 0x0369 + deviceProfileName: zooz-zse50 - id: "Everspring" deviceLabel: Everspring Siren manufacturerId: 0x0060 diff --git a/drivers/SmartThings/zwave-siren/profiles/zooz-zse50.yml b/drivers/SmartThings/zwave-siren/profiles/zooz-zse50.yml new file mode 100644 index 0000000000..866b88f436 --- /dev/null +++ b/drivers/SmartThings/zwave-siren/profiles/zooz-zse50.yml @@ -0,0 +1,309 @@ +# Zooz ZSE50 Siren/Chime +# With deviceConfig - allows setting a tone from routines +name: zooz-zse50 +components: + - id: main + capabilities: + - id: alarm + version: 1 + - id: chime + version: 1 + - id: mode + version: 1 + - id: powerSource + version: 1 + - id: audioVolume + version: 1 + - id: battery + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: Siren +### PREFERENCES ### +preferences: + #param 1 + - name: "playbackMode" + title: "Playback Mode" + description: "* = Default; Set siren playback mode: once (0), loop for x seconds (1), loop x times (2), loop until cancel (3), no sound (4)." + required: false + preferenceType: enumeration + definition: + options: + 0: "Play once *" + 1: "Play in loop for set duration" + 2: "Play in loop for set number" + 3: "Play in loop until stopped" + 4: "No sound, LED only" + default: 0 + #param 2 + - name: "playbackDuration" + title: "Playback Duration" + description: "Default: 180; Set playback duration for the siren (in seconds) when the siren is in playback mode 1." + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 900 + default: 180 + #param 3 + - name: "playbackLoop" + title: "Playback Loop Count" + description: "Default: 1; Set the number of playback loops for the selected tone when the siren is in playback mode 2." + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 99 + default: 1 + #param 4 + - name: "playbackTone" + title: "Playback Tone" + description: "Set the default tone for the siren playback. Choose the number of the file in the library as value. Check the 'modes' list for the id numbers" + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 50 + default: 1 + #param 5 - playbackVolume ## Handled with volume command + #param 6 + - name: "ledMode" + title: "LED Indicator Mode" + description: "* = Default; Set the LED indicator mode for the siren: off (0), strobe (1), police strobe (2), pulse (3), solid on (4). See documentation for details." + required: false + preferenceType: enumeration + definition: + options: + 0: "LED always off" + 1: "LED strobe single color *" + 2: "LED strobe red and blue" + 3: "LED pulse single color" + 4: "LED solid on single color" + default: 1 + #param 7 + - name: "ledColor" + title: "LED Indicator Color" + description: "Default: 0; Set the LED indicator color: red (0), yellow (42), green (85), indigo (127), blue (170), purple (212), or white (255). More colors available through custom values corresponding to the color wheel. See advanced documentation for details." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 0 + #param 8 + - name: "lowBattery" + title: "Low Battery Report" + description: "Which % level should the device report low battery to the hub." + required: false + preferenceType: enumeration + definition: + options: + 10: "10% [DEFAULT]" + 15: "15%" + 20: "20%" + 25: "25%" + 30: "30%" + 35: "35%" + 40: "40%" + default: 10 + #param 9 + - name: "ledBatteryMode" + title: "LED In Back-Up Battery Mode" + description: "* = Default; Set the LED indicator in back-up battery mode: off (0), regular LED mode (1), pulse white for full battery and red for low battery (2)." + required: false + preferenceType: enumeration + definition: + options: + 0: "LED off" + 1: "Regular LED mode *" + 2: "Pulse white for full, red for low" + default: 1 + #param 10 + - name: "btnToneSelection" + title: "Button Tone Selection" + description: "Disable tone selection from physical buttons on the siren (0). When disabled, you'll only be able to program tones using the advanced parameters in the Z-Wave UI. Expert users only, see documentation for details." + required: false + preferenceType: enumeration + definition: + options: + 0: "Disabled" + 1: "Enabled [DEFAULT]" + default: 1 + #param 11 + - name: "btnVolSelection" + title: "Button Volume Selection" + description: "Disable volume adjustment from physical buttons on the siren (0). When disabled, you'll only be able to adjust volume using the advanced parameters in the Z-Wave UI. Expert users only, see documentation for details." + required: false + preferenceType: enumeration + definition: + options: + 0: "Disabled" + 1: "Enabled [DEFAULT]" + default: 1 + #param 13 + - name: "systemVolume" + title: "System Message Volume" + description: "Default: 50; Set system message volume (0-100, 0 – mute)." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 100 + default: 50 + #param 14 + - name: "ledBrightness" + title: "LED Indicator Brightness" + description: "Default: 5; Choose the LED indicator's brightness level (0 – off, 10 – high brightness)." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 10 + default: 5 + #param 15 + - name: "batteryFrequency" + title: "Battery Reporting Frequency" + description: "Default: 12; Set the reporting interval for battery (1-84 hours)." + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 84 + default: 12 + #param 16 + - name: "batteryThreshold" + title: "Battery Reporting Threshold" + description: "Default: 0; Set the threshold for battery reporting in % changes. Set to 0 to disable reporting based on threshold." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 20 + default: 0 + +### DEVICE CONFIG ### +deviceConfig: + dashboard: + states: + - component: main + capability: chime + version: 1 + actions: + - component: main + capability: chime + version: 1 + basicPlus: [ ] + detailView: + - component: main + capability: alarm + version: 1 + values: + - key: alarm.value + enabledValues: + - 'off' + - 'both' + - key: "{{enumCommands}}" + enabledValues: + - 'off' + - 'both' + - component: main + capability: chime + version: 1 + - component: main + capability: mode + version: 1 + - component: main + capability: powerSource + version: 1 + values: + - key: powerSource.value + enabledValues: + - 'battery' + - 'mains' + - component: main + capability: audioVolume + version: 1 + - component: main + capability: battery + version: 1 + - component: main + capability: refresh + version: 1 + automation: + conditions: + - component: main + capability: alarm + version: 1 + values: + - key: alarm.value + enabledValues: + - 'off' + - 'both' + - component: main + capability: chime + version: 1 + - component: main + capability: mode + version: 1 + patch: + - op: replace + path: /0/displayType + value: dynamicList + - op: add + path: /0/dynamicList + value: + value: mode.value + command: setMode + supportedValues: + value: supportedArguments.value + - op: remove + path: /0/list + - component: main + capability: powerSource + version: 1 + values: + - key: powerSource.value + enabledValues: + - 'battery' + - 'mains' + step: 1 + - component: main + capability: audioVolume + version: 1 + - component: main + capability: battery + version: 1 + actions: + - component: main + capability: alarm + version: 1 + values: + - key: "{{enumCommands}}" + enabledValues: + - 'off' + - 'both' + - component: main + capability: chime + version: 1 + - component: main + capability: mode + version: 1 + patch: + - op: replace + path: /0/displayType + value: dynamicList + - op: add + path: /0/dynamicList + value: + value: mode.value + command: setMode + supportedValues: + value: supportedArguments.value + - op: remove + path: /0/list + - component: main + capability: audioVolume + version: 1 diff --git a/drivers/SmartThings/zwave-siren/src/init.lua b/drivers/SmartThings/zwave-siren/src/init.lua index a725e8b79c..c217cde155 100644 --- a/drivers/SmartThings/zwave-siren/src/init.lua +++ b/drivers/SmartThings/zwave-siren/src/init.lua @@ -90,10 +90,14 @@ local driver_template = { capabilities.tamperAlert, capabilities.temperatureMeasurement, capabilities.relativeHumidityMeasurement, - capabilities.chime + capabilities.chime, + capabilities.powerSource, + capabilities.audioVolume, + capabilities.firmwareUpdate, }, sub_drivers = { require("multifunctional-siren"), + require("zooz-zse50"), require("zwave-sound-sensor"), require("ecolink-wireless-siren"), require("philio-sound-siren"), diff --git a/drivers/SmartThings/zwave-siren/src/preferences.lua b/drivers/SmartThings/zwave-siren/src/preferences.lua index 3e9f8333ab..4d71bf2fc8 100644 --- a/drivers/SmartThings/zwave-siren/src/preferences.lua +++ b/drivers/SmartThings/zwave-siren/src/preferences.lua @@ -56,7 +56,32 @@ local devices = { PARAMETERS = { alarmLength = {parameter_number = 1, size = 2} } - } + }, + ZOOZ_ZSE50_SIREN = { + MATCHING_MATRIX = { + mfrs = 0x027A, + product_types = 0x0004, + product_ids = 0x0369 + }, + PARAMETERS = { + playbackMode = { parameter_number = 1, size = 1 }, + playbackDuration = { parameter_number = 2, size = 2 }, + playbackLoop = { parameter_number = 3, size = 1 }, + playbackTone = { parameter_number = 4, size = 1 }, + playbackVolume = { parameter_number = 5, size = 1 }, + ledMode = { parameter_number = 6, size = 1 }, + ledColor = { parameter_number = 7, size = 1 }, + lowBattery = { parameter_number = 8, size = 1 }, + ledBatteryMode = { parameter_number = 9, size = 1 }, + btnToneSelection = { parameter_number = 10, size = 1 }, + btnVolSelection = { parameter_number = 11, size = 1 }, + basicSetGrp2 = { parameter_number = 12, size = 1 }, --Not Used + systemVolume = { parameter_number = 13, size = 1 }, + ledBrightness = { parameter_number = 14, size = 1 }, + batteryFrequency = { parameter_number = 15, size = 1 }, + batteryThreshold = { parameter_number = 16, size = 1 } + } + }, } local preferences = {} @@ -80,4 +105,5 @@ preferences.to_numeric_value = function(new_value) end return numeric end + return preferences diff --git a/drivers/SmartThings/zwave-siren/src/zooz-zse50/init.lua b/drivers/SmartThings/zwave-siren/src/zooz-zse50/init.lua new file mode 100644 index 0000000000..b3b2fcc491 --- /dev/null +++ b/drivers/SmartThings/zwave-siren/src/zooz-zse50/init.lua @@ -0,0 +1,409 @@ +-- Copyright 2022 SmartThings +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local preferencesMap = require "preferences" + +local log = require "log" +local st_utils = require "st.utils" +local capabilities = require "st.capabilities" +local defaults = require "st.zwave.defaults" + +local cc = require "st.zwave.CommandClass" +local Basic = (require "st.zwave.CommandClass.Basic")({ version = 1 }) +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 8 }) +local SoundSwitch = (require "st.zwave.CommandClass.SoundSwitch")({ version = 1 }) +local Version = (require "st.zwave.CommandClass.Version")({ version = 1 }) + +local ZSE50_DEFAULT_PROFILE = "zooz-zse50" +local ZSE50_FINGERPRINTS = { + { manufacturerId = 0x027A, productType = 0x0004, productId = 0x0369 } -- Zooz ZSE50 Siren & Chime +} + +--- @param driver Driver driver instance +--- @param device Device device instance +--- @return boolean true if the device proper, else false +local function can_handle_zooz_zse50(opts, driver, device, ...) + for _, fingerprint in ipairs(ZSE50_FINGERPRINTS) do + if device:id_match(fingerprint.manufacturerId, fingerprint.productType, fingerprint.productId) then + return true + end + end + return false +end + +--- @param self st.zwave.Driver +--- @param device st.zwave.Device +local function update_firmwareUpdate_capability(self, device, component, major, minor) + if device:supports_capability_by_id(capabilities.firmwareUpdate.ID, component.id) then + local fmtFirmwareVersion = string.format("%d.%02d", major, minor) + device:emit_component_event(component, capabilities.firmwareUpdate.currentVersion({ value = fmtFirmwareVersion })) + end +end + +--- Update the built in capability firmwareUpdate's currentVersion attribute with the +--- Zwave version information received during pairing of the device. +--- @param self st.zwave.Driver +--- @param device st.zwave.Device +local function updateFirmwareVersion(self, device) + local fw_major = (((device.st_store or {}).zwave_version or {}).firmware or {}).major + local fw_minor = (((device.st_store or {}).zwave_version or {}).firmware or {}).minor + if fw_major and fw_minor then + update_firmwareUpdate_capability(self, device, device.profile.components.main, fw_major, fw_minor) + else + device.log.warn("Firmware major or minor version not available.") + end +end + +local function playTone(device, tone_id) + local tones_duration = device:get_field("TONES_DURATION") + local default_tone = device:get_field("TONE_DEFAULT") + local duration = tones_duration[tonumber(tone_id)] + local playbackMode = tonumber(device.preferences.playbackMode) + if tone_id > 0 then + if tone_id == 0xFF then + duration = tones_duration[tonumber(default_tone)] + end + if playbackMode == 1 then + duration = device.preferences.playbackDuration + elseif playbackMode == 2 then + duration = duration * device.preferences.playbackLoop + end + end + log.debug(string.format("Playing Tone: %s, playbackMode %s, duration %ss", tone_id, playbackMode, duration)) + + device:send(SoundSwitch:TonePlaySet({ tone_identifier = tone_id })) + device:send(SoundSwitch:TonePlayGet({})) + + local soundSwitch_refresh = function() + local chime = device:get_latest_state("main", capabilities.chime.ID, capabilities.chime.chime.NAME) + local mode = device:get_latest_state("main", capabilities.mode.ID, capabilities.mode.mode.NAME) + log.debug(string.format("soundSwitch_refresh: %s | %s", chime, mode)) + if chime ~= "off" or mode ~= "Off" then + device:send(SoundSwitch:TonePlayGet({})) + end + end + + if tone_id > 0 and playbackMode <= 2 then + local minDuration = math.max(duration, 4) + device.thread:call_with_delay(minDuration + 0.5, soundSwitch_refresh) + device.thread:call_with_delay(minDuration + 4, soundSwitch_refresh) + end + +end + +local function rebuildTones(device) + device:emit_event(capabilities.mode.mode("Rebuild List")) + device:send(SoundSwitch:TonesNumberGet({})) +end + +local function refresh_handler(self, device) + log.debug("***DEBUG*** refresh_handler (zse50)") + device:default_refresh() + device:send(Version:Get({})) + device:send(Notification:Get({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.STATE_IDLE, + v1_alarm_type = 0 + })) + device:send(SoundSwitch:ConfigurationGet({})) + device:send(SoundSwitch:TonePlayGet({})) +end + +local function setMode_handler(self, device, command) + local mode_value = command.args.mode + local mode_split = string.find(mode_value, ":") + + if mode_split ~= nil then + mode_value = string.sub(mode_value, 1, mode_split - 1) + end + log.debug(string.format("***DEBUG*** setMode_handler (%s)", mode_value)) + + if mode_value == 'Rebuild List' then + rebuildTones(device) + elseif mode_value == 'Off' then + playTone(device, 0x00) + else + playTone(device, tonumber(mode_value)) + end +end + +local function setVolume_handler(self, device, cmd) + local new_volume = st_utils.clamp_value(cmd.args.volume, 0, 100) + device:send(SoundSwitch:ConfigurationSet({ volume = new_volume })) +end + +local function volumeUp_handler(self, device, cmd) + local volume = device:get_latest_state("main", capabilities.audioVolume.ID, capabilities.audioVolume.volume.NAME) + volume = st_utils.clamp_value(volume + 2, 0, 100) + device:send(SoundSwitch:ConfigurationSet({ volume = volume })) +end + +local function volumeDown_handler(self, device, cmd) + local volume = device:get_latest_state("main", capabilities.audioVolume.ID, capabilities.audioVolume.volume.NAME) + volume = st_utils.clamp_value(volume - 2, 0, 100) + device:send(SoundSwitch:ConfigurationSet({ volume = volume })) +end + +local function tone_on(self, device) + playTone(device, 0xFF) +end + +local function tone_off(self, device) + playTone(device, 0x00) +end + +local function tones_number_report_handler(self, device, cmd) + local total_tones = cmd.args.supported_tones + log.debug("***DEBUG*** tones_number_report_handler... " .. total_tones) + + --Max 50 tones per Zooz settings + if total_tones > 50 then + total_tones = 50 + end + + local tones_list = { } + local tones_duration = { } + device:set_field("TOTAL_TONES", total_tones) + device:set_field("TONES_LIST_TMP", tones_list) + device:set_field("TONES_DURATION_TMP", tones_duration) + + --Get info on all tones + for tone = 1, total_tones do + device:send(SoundSwitch:ToneInfoGet({ tone_identifier = tone })) + end +end + +local function tone_info_report_handler(self, device, cmd) + local tone_id = tonumber(cmd.args.tone_identifier) + local tone_name = cmd.args.name + local duration = cmd.args.tone_duration + local total_tones = device:get_field("TOTAL_TONES") + local tones_list = device:get_field("TONES_LIST_TMP") or {} + local tones_duration = device:get_field("TONES_DURATION_TMP") or {} + log.debug(string.format("***DEBUG*** tone_info_report_handler... %s:%s (%ss)", tone_id, tone_name, duration)) + + --table.insert(tones_list, string.format("%s: %s (%ss)", tone_id, tone_name, duration)) + tones_list[tone_id] = string.format("%s: %s (%ss)", tone_id, tone_name, duration) + tones_duration[tone_id] = duration + device:set_field("TONES_LIST_TMP", tones_list) + device:set_field("TONES_DURATION_TMP", tones_duration) + + if tone_id >= total_tones or #tones_duration >= total_tones then + log.debug(string.format("Got info on all tones... #tones_duration %s, #tones_list %s, total_tones %s", #tones_duration, #tones_list, total_tones)) + device:set_field("TONES_LIST", tones_list, { persist = true }) + device:set_field("TONES_DURATION", tones_duration, { persist = true }) + + local tones_arguments = { "Off" } + for il, vl in ipairs(tones_list) do + --log.debug(string.format("#%s:: '%s' // '%s'", il, tones_list[il], vl)) + table.insert(tones_arguments, vl) + end + + device:emit_event(capabilities.mode.supportedModes({ "Rebuild List", table.unpack(tones_arguments) })) + device:emit_event(capabilities.mode.supportedArguments(tones_arguments)) + device:send(SoundSwitch:TonePlayGet({})) + + end +end + +--- Handle when tone is played (TONE_PLAY_REPORT or BASIC_REPORT) +local function tone_playing(self, device, tone_id) + local tones_list = device:get_field("TONES_LIST") + log.debug(string.format("***DEBUG*** tone_playing... id: %s", tone_id)) + + if device:get_latest_state("main", capabilities.mode.ID, capabilities.mode.supportedModes.NAME) == nil then + rebuildTones(device) + end + + if tone_id == 0 then + device:emit_event(capabilities.alarm.alarm.off()) + device:emit_event(capabilities.chime.chime.off()) + device:emit_event(capabilities.mode.mode("Off")) + else + local tone_name = tones_list[tone_id] or tostring(tone_id) + device:emit_event(capabilities.alarm.alarm.both()) + device:emit_event(capabilities.chime.chime.chime()) + device:emit_event(capabilities.mode.mode(tone_name)) + end +end + +local function tone_play_report_handler(self, device, cmd) + local tone_id = tonumber(cmd.args.tone_identifier) + local tone_volume = cmd.args.play_command_tone_volume + log.debug(string.format("***DEBUG*** tone_play_report_handler... id: %s, vol: %s", tone_id, tone_volume)) + tone_playing(self, device, tone_id) +end + +local function basic_report_handler(self, device, cmd) + local tone_id = tonumber(cmd.args.value) + log.debug(string.format("***DEBUG*** basic_report_handler... value: %s", tone_id)) + tone_playing(self, device, tone_id) +end + +--- Handle SoundSwitch Config Reports (volume) +local function soundSwitch_configuration_report(self, device, cmd) + local volume = st_utils.clamp_value(cmd.args.volume, 0, 100) + local default_tone = cmd.args.default_tone_identifer + log.debug(string.format("***DEBUG*** soundSwitch_configuration_report... vol: %s", volume)) + device:emit_event(capabilities.audioVolume.volume(volume)) + device:set_field("TONE_DEFAULT", default_tone, { persist = true }) +end + +--- Handle power source changes +local function notification_report_handler(self, device, cmd) + if cmd.args.notification_type == Notification.notification_type.POWER_MANAGEMENT then + local event = cmd.args.event + local powerManagement = Notification.event.power_management + + if event == powerManagement.AC_MAINS_DISCONNECTED then + device:emit_event(capabilities.powerSource.powerSource.battery()) + elseif event == powerManagement.AC_MAINS_RE_CONNECTED or event == powerManagement.STATE_IDLE then + device:emit_event(capabilities.powerSource.powerSource.mains()) + end + end +end + +--- @param driver st.zwave.Driver +--- @param device st.zwave.Device +--- @param cmd st.zwave.CommandClass.Version.Report +local function version_report_handler(driver, device, cmd) + log.debug("***DEBUG*** version_report_handler...") + local major = cmd.args.application_version + local minor = cmd.args.application_sub_version + + -- Update the built in firmware capability, if available + update_firmwareUpdate_capability(driver, device, device.profile.components.main, major, minor) +end + +--- @param driver st.zwave.Driver +--- @param device st.zwave.Device +local function device_init(driver, device) + device:send(Version:Get({})) + device:try_update_metadata({ profile = ZSE50_DEFAULT_PROFILE }) + + if (device:get_field("TONES_DURATION") == nil or device:get_field("TONE_DEFAULT") == nil) then + rebuildTones(device) + end +end + +--- @param driver st.zwave.Driver +--- @param device st.zwave.Device +local function device_added(driver, device) + device:send(SoundSwitch:ConfigurationSet({ volume = 10 })) + updateFirmwareVersion(driver, device) + device:refresh() +end + +--- Handle preference changes (same as default but added hack for unsigned parameters) +--- +--- @param driver st.zwave.Driver +--- @param device st.zwave.Device +--- @param event table +--- @param args +local function info_changed(driver, device, event, args) + local preferences = preferencesMap.get_device_parameters(device) + + if preferences then + local did_configuration_change = false + for id, value in pairs(device.preferences) do + if args.old_st_store.preferences[id] ~= value and preferences[id] then + local new_parameter_value = preferencesMap.to_numeric_value(device.preferences[id]) + --Hack to convert to signed integer + local size_factor = math.floor(256 ^ preferences[id].size) + if new_parameter_value >= (size_factor / 2) then + new_parameter_value = new_parameter_value - size_factor + end + --END Hack + device:send(Configuration:Set({ parameter_number = preferences[id].parameter_number, size = preferences[id].size, configuration_value = new_parameter_value })) + did_configuration_change = true + end + end + + if did_configuration_change then + local delayed_command = function() + device:send(Basic:Set({ value = 0x00 })) + end + device.thread:call_with_delay(1, delayed_command) + end + + end +end + +local zooz_zse50 = { + NAME = "Zooz ZSE50", + can_handle = can_handle_zooz_zse50, + + supported_capabilities = { + capabilities.battery, + capabilities.chime, + capabilities.mode, + capabilities.audioVolume, + capabilities.powerSource, + capabilities.firmwareUpdate, + capabilities.configuration, + capabilities.refresh + }, + + zwave_handlers = { + [cc.BASIC] = { + [Basic.REPORT] = basic_report_handler + }, + [cc.SOUND_SWITCH] = { + [SoundSwitch.TONES_NUMBER_REPORT] = tones_number_report_handler, + [SoundSwitch.TONE_INFO_REPORT] = tone_info_report_handler, + [SoundSwitch.TONE_PLAY_REPORT] = tone_play_report_handler, + [SoundSwitch.CONFIGURATION_REPORT] = soundSwitch_configuration_report + }, + [cc.NOTIFICATION] = { + [Notification.REPORT] = notification_report_handler + }, + [cc.VERSION] = { + [Version.REPORT] = version_report_handler + } + }, + + capability_handlers = { + [capabilities.mode.ID] = { + [capabilities.mode.commands.setMode.NAME] = setMode_handler + }, + [capabilities.audioVolume.ID] = { + [capabilities.audioVolume.commands.setVolume.NAME] = setVolume_handler, + [capabilities.audioVolume.commands.volumeUp.NAME] = volumeUp_handler, + [capabilities.audioVolume.commands.volumeDown.NAME] = volumeDown_handler + }, + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = refresh_handler + }, + [capabilities.alarm.ID] = { + [capabilities.alarm.commands.both.NAME] = tone_on, + [capabilities.alarm.commands.off.NAME] = tone_off + }, + [capabilities.chime.ID] = { + [capabilities.chime.commands.chime.NAME] = tone_on, + [capabilities.chime.commands.off.NAME] = tone_off + }, + + }, + + lifecycle_handlers = { + init = device_init, + added = device_added, + infoChanged = info_changed + } +} + +defaults.register_for_default_handlers(zooz_zse50, zooz_zse50.supported_capabilities) + +return zooz_zse50 From 3b569c88083821201cb00c220825ccd060f062db Mon Sep 17 00:00:00 2001 From: jtp10181 Date: Thu, 1 Jan 2026 22:26:22 -0600 Subject: [PATCH 2/2] Fix fingerprint deviceLabel for ZSE50 --- drivers/SmartThings/zwave-siren/fingerprints.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zwave-siren/fingerprints.yml b/drivers/SmartThings/zwave-siren/fingerprints.yml index c4f6fdedea..4b25211366 100644 --- a/drivers/SmartThings/zwave-siren/fingerprints.yml +++ b/drivers/SmartThings/zwave-siren/fingerprints.yml @@ -6,7 +6,7 @@ zwaveManufacturer: productId: 0x0003 deviceProfileName: multifunctional-siren - id: "Zooz/ZSE50" - deviceLabel: Zooz ZSE50 Siren & Chime + deviceLabel: Zooz ZSE50 Siren and Chime manufacturerId: 0x027A productType: 0x0004 productId: 0x0369