Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions drivers/SmartThings/zwave-sensor/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -502,12 +502,12 @@ zwaveManufacturer:
productType: 0x4C47
productId: 0x4C44
deviceProfileName: base-water
- id: 027A/7000/E002
deviceLabel: Zooz Water Leak Sensor
- id: "Zooz/ZSE42"
deviceLabel: Zooz ZSE42 XS Water Leak Sensor
manufacturerId: 0x027A
productType: 0x7000
productId: 0xE002
deviceProfileName: base-water
deviceProfileName: zooz-zse42-water
- id: 010F/0800
deviceLabel: Fibaro Motion Sensor
manufacturerId: 0x010F
Expand Down
52 changes: 52 additions & 0 deletions drivers/SmartThings/zwave-sensor/profiles/zooz-zse42-water.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: zooz-zse42-water
components:
- id: main
capabilities:
- id: waterSensor
version: 1
- id: battery
version: 1
- id: refresh
version: 1
- id: firmwareUpdate
version: 1
categories:
- name: LeakSensor
preferences:
#param 1
- name: "ledIndicator"
title: "LED Indicator"
description: "When enabled the LED indicator will blink continuously when water is detected."
required: false
preferenceType: enumeration
definition:
options:
1: "Enabled *"
0: "Disabled"
default: 1
#param 2
- name: "leakAlertClearDelay"
title: "Leak Alert Clear Delay"
description: "Default = 0; How long the sensor will wait before sending a 'dry' report to your hub after water is no longer detected."
required: false
preferenceType: integer
definition:
minimum: 0
maximum: 3600
default: 0
#param 4
- name: "lowBatteryAlert"
title: "Low Battery Alert"
description: "Battery level threshold for low battery reports."
required: false
preferenceType: enumeration
definition:
options:
10: "10%"
15: "15%"
20: "20% *"
25: "25%"
30: "30%"
40: "40%"
50: "50%"
default: 20
32 changes: 32 additions & 0 deletions drivers/SmartThings/zwave-sensor/src/configurations.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.

local capabilities = require "st.capabilities"
--- @type st.zwave.CommandClass.Configuration
local Configuration = (require "st.zwave.CommandClass.Configuration")({ version=4 })
--- @type st.zwave.CommandClass.Association
Expand Down Expand Up @@ -345,6 +346,20 @@ local devices = {
ASSOCIATION = {
{grouping_identifier = 1}
}
},
ZOOZ_ZSE42_WATER_LEAK = {
MATCHING_MATRIX = {
mfrs = 0x027A,
product_types = 0x7000,
product_ids = 0xE002
},
BATTERY = {
quantity = 1,
type = "CR2450"
},
WAKE_UP = {
{ seconds = 21600 } --6 hours
}
}
}
local configurations = {}
Expand Down Expand Up @@ -376,6 +391,11 @@ configurations.initial_configuration = function(driver, device)
device:send(WakeUp:IntervalSet({seconds = value.seconds, node_id = _node_id}))
end
end
local battery = configurations.get_device_battery(device)
if battery ~= nil then
device:emit_event(capabilities.battery.quantity({ value = battery.quantity or 1 }))
device:emit_event(capabilities.battery.type({ value = battery.type or "Unspecified" }))
end
end

configurations.get_device_configuration = function(zw_device)
Expand Down Expand Up @@ -426,4 +446,16 @@ configurations.get_device_wake_up = function(zw_device)
return nil
end

configurations.get_device_battery = function(zw_device)
for _, device in pairs(devices) do
if zw_device:id_match(
device.MATCHING_MATRIX.mfrs,
device.MATCHING_MATRIX.product_types,
device.MATCHING_MATRIX.product_ids) then
return device.BATTERY
end
end
return nil
end

return configurations
91 changes: 91 additions & 0 deletions drivers/SmartThings/zwave-sensor/src/firmware-version/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
-- Copyright 2025 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 capabilities = require "st.capabilities"
--- @type st.zwave.CommandClass
local cc = require "st.zwave.CommandClass"
--- @type st.zwave.CommandClass.Version
local Version = (require "st.zwave.CommandClass.Version")({ version = 1 })
--- @type st.zwave.CommandClass.WakeUp
local WakeUp = (require "st.zwave.CommandClass.WakeUp")({ version = 1 })

--This sub_driver will populate the currentVersion (firmware) when the firmwareUpdate capability is enabled
local FINGERPRINTS = {
{ manufacturerId = 0x027A, productType = 0x7000, productId = 0xE002 } -- Zooz ZSE42 Water Sensor
}

local function can_handle_fw(opts, driver, device, ...)
if device:supports_capability_by_id(capabilities.firmwareUpdate.ID) then
for _, fingerprint in ipairs(FINGERPRINTS) do
if device:id_match(fingerprint.manufacturerId, fingerprint.productType, fingerprint.productId) then
local subDriver = require("firmware-version")
return true, subDriver
end
end
end
return false
end

--Runs upstream handlers (ex zwave_handlers)
local function call_parent_handler(handlers, self, device, event, args)
for _, func in ipairs(handlers or {}) do
func(self, device, event, args)
end
end

--Request version if not populated yet
local function send_version_get(driver, device)
if device:get_latest_state("main", capabilities.firmwareUpdate.ID, capabilities.firmwareUpdate.currentVersion.NAME) == nil then
device:send(Version:Get({}))
end
end

local function version_report(driver, device, cmd)
local major = cmd.args.application_version
local minor = cmd.args.application_sub_version
local fmtFirmwareVersion = string.format("%d.%02d", major, minor)
device:emit_event(capabilities.firmwareUpdate.currentVersion({ value = fmtFirmwareVersion }))
end

local function wakeup_notification(driver, device, cmd)
send_version_get(driver, device)
--Call parent WakeUp functions
call_parent_handler(driver.zwave_handlers[cc.WAKE_UP][WakeUp.NOTIFICATION], driver, device, cmd)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to see a unit test to verify this works

Copy link
Author

@jtp10181 jtp10181 Jan 23, 2026

Choose a reason for hiding this comment

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

Added unit test with commit b36c150
It checks that both the commands in the sub-driver and the main driver are sent. Ran test myself and it passed.

end

local function device_init(driver, device)
--Call main init function
driver.lifecycle_handlers.init(driver, device)
--Extras for this sub_driver
send_version_get(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.

It often happens that, on hub restart, the init lifecycle event can be sent before the radio is fully initialized, resulting in radio send failures. Since you are only sending the get if the fw version is unpopulated, I would recommend moving this to the added handler.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or just leaving it exclusive to the wakeup handler. It may not get populated immediately, but will be eventually.

Copy link
Author

Choose a reason for hiding this comment

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

Moved to added handler and tested a fresh include, it picked up the version.

end

local firmware_version = {
NAME = "firmware_version",
can_handle = can_handle_fw,

lifecycle_handlers = {
init = device_init,
},
zwave_handlers = {
[cc.VERSION] = {
[Version.REPORT] = version_report
},
[cc.WAKE_UP] = {
[WakeUp.NOTIFICATION] = wakeup_notification
}
}
}

return firmware_version
1 change: 1 addition & 0 deletions drivers/SmartThings/zwave-sensor/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ local driver_template = {
lazy_load_if_possible("v1-contact-event"),
lazy_load_if_possible("timed-tamper-clear"),
lazy_load_if_possible("wakeup-no-poll"),
lazy_load_if_possible("firmware-version"),
lazy_load_if_possible("apiv6_bugfix"),
},
lifecycle_handlers = {
Expand Down
13 changes: 13 additions & 0 deletions drivers/SmartThings/zwave-sensor/src/preferences.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@ local devices = {
motionNotdetRepT = {parameter_number = 160, size = 2},
},
},
ZOOZ_ZSE42_WATER_LEAK = {
MATCHING_MATRIX = {
mfrs = 0x027A,
product_types = 0x7000,
product_ids = 0xE002
},
PARAMETERS = {
ledIndicator = { parameter_number = 1, size = 1 },
leakAlertClearDelay = { parameter_number = 2, size = 4 },
batteryThreshold = { parameter_number = 3, size = 1 },
lowBatteryAlert = { parameter_number = 4, size = 1 },
},
},
}
local preferences = {}

Expand Down