Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions drivers/Unofficial/tuya-zigbee/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ zigbeeManufacturer:
manufacturer: _TZE284_fziifcxj
model: TS0601
deviceProfileName: thermostat
- id: _TZ3000_dvagjie2/TS0207
deviceLabel: Tuya Water Leak Sensor
manufacturer: _TZ3000_dvagjie2
model: TS0207
deviceProfileName: water-battery
14 changes: 14 additions & 0 deletions drivers/Unofficial/tuya-zigbee/profiles/water-battery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: water-battery
components:
- id: main
capabilities:
- id: waterSensor
version: 1
- id: battery
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: LeakSensor
3 changes: 2 additions & 1 deletion drivers/Unofficial/tuya-zigbee/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ local unofficial_tuya_driver_template = {
require("motion-sensor"),
require("smoke-detector"),
require("switch"),
require("thermostat")
require("thermostat"),
require("water-leak")
},
health_check = false,
}
Expand Down
113 changes: 113 additions & 0 deletions drivers/Unofficial/tuya-zigbee/src/test/test_tuya_water_leak.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

-- Mock out globals
local test = require "integration_test"
local t_utils = require "integration_test.utils"
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
local capabilities = require "st.capabilities"
local clusters = require "st.zigbee.zcl.clusters"

local mock_simple_device = test.mock_device.build_test_zigbee_device(
{
profile = t_utils.get_profile_definition("water-battery.yml"),
zigbee_endpoints = {
[1] = {
id = 1,
manufacturer = "_TZ3000_dvagjie2",
model = "TS0207",
server_clusters = { clusters.IASZone.ID }
}
}
}
)

zigbee_test_utils.prepare_zigbee_env_info()

local function test_init()
test.mock_device.add_test_device(mock_simple_device)
end

test.set_test_init_function(test_init)

test.register_coroutine_test(
"Handle added lifecycle event",
function()
test.socket.device_lifecycle:__queue_receive({ mock_simple_device.id, "added" })

test.socket.capability:__expect_send(
mock_simple_device:generate_test_message(
"main",
capabilities.waterSensor.water.dry()
)
)

test.socket.capability:__expect_send(
mock_simple_device:generate_test_message(
"main",
capabilities.battery.battery(100)
)
)
end,
{}
)

test.register_message_test(
"Handle IASZone status change (dry)",
{
{
channel = "zigbee",
direction = "receive",
message = {
mock_simple_device.id,
clusters.IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(
mock_simple_device, -- device
0x00, -- zone_status (dry)
0x00, -- extended_status
0x00, -- zone_id
0x0000 -- delay
)
}
},
{
channel = "capability",
direction = "send",
message = mock_simple_device:generate_test_message(
"main",
capabilities.waterSensor.water.dry()
)
}
},
{}
)

test.register_message_test(
"Handle IASZone status change (wet)",
{
{
channel = "zigbee",
direction = "receive",
message = {
mock_simple_device.id,
clusters.IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(
mock_simple_device, -- device
0x01, -- zone_status (wet)
0x00, -- extended_status
0x00, -- zone_id
0x0000 -- delay
)
}
},
{
channel = "capability",
direction = "send",
message = mock_simple_device:generate_test_message(
"main",
capabilities.waterSensor.water.wet()
)
}
},
{}
)

test.run_registered_tests()
78 changes: 78 additions & 0 deletions drivers/Unofficial/tuya-zigbee/src/water-leak/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

local capabilities = require "st.capabilities"
local defaults = require "st.zigbee.defaults"
local clusters = require "st.zigbee.zcl.clusters"

local FINGERPRINTS = {
{ mfr = "_TZ3000_dvagjie2", model = "TS0207"}
}

local function is_tuya_water_leak(opts, driver, device)
for _, fingerprint in ipairs(FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
return true
end
end
return false
end

local function device_added(self, device)
device:emit_event(capabilities.waterSensor.water.dry())
device:emit_event(capabilities.battery.battery(100))
end

local generate_event_from_zone_status = function(driver, device, zone_status, zb_rx)
local event

if zone_status:is_alarm1_set() then
event = capabilities.waterSensor.water.wet()
else
event = capabilities.waterSensor.water.dry()
end

if event ~= nil then
device:emit_event(event)
end
end

local ias_zone_status_change_handler = function(driver, device, zb_rx)
generate_event_from_zone_status(driver, device, zb_rx.body.zcl_body.zone_status, zb_rx)
end

local ias_zone_status_attr_handler = function(driver, device, zone_status, zb_rx)
generate_event_from_zone_status(driver, device, zone_status, zb_rx)
end

local tuya_water_leak_driver = {
NAME = "tuya water leak",
supported_capabilities = {
capabilities.waterSensor,
capabilities.battery,
capabilities.refresh
},
zigbee_handlers = {
attr = {
[clusters.IASZone.ID] = {
[clusters.IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler
}
},
cluster = {
[clusters.IASZone.ID] = {
[clusters.IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler
}
}
},
lifecycle_handlers = {
added = device_added
},
can_handle = is_tuya_water_leak
}

defaults.register_for_default_handlers(
tuya_water_leak_driver,
tuya_water_leak_driver.supported_capabilities,
{}
)
return tuya_water_leak_driver
Loading