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
11 changes: 10 additions & 1 deletion drivers/SmartThings/zigbee-valve/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,14 @@ zigbeeManufacturer :
manufacturer: Compacta
model: ZBVC1(1023A)
deviceProfileName: valve-battery-powerSource

- id: SONOFF/SWV-ZF2U
deviceLabel: SONOFF Water Valve 2
manufacturer: SONOFF
model: SWV-ZF2U
deviceProfileName: valve-battery-2
- id: SONOFF/SWV-ZF2
deviceLabel: SONOFF Water Valve 2
manufacturer: SONOFF
model: SWV-ZF2
deviceProfileName: valve-battery-2

21 changes: 21 additions & 0 deletions drivers/SmartThings/zigbee-valve/profiles/valve-battery-2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: valve-battery-2
components:
- id: main
capabilities:
- id: valve
version: 1
- id: battery
version: 1
- id: powerSource
version: 1
config:
values:
- key: "powerSource.value"
enabledValues:
- battery
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: WaterValve
10 changes: 10 additions & 0 deletions drivers/SmartThings/zigbee-valve/profiles/valve.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: valve
components:
- id: main
capabilities:
- id: valve
version: 1
- id: refresh
version: 1
categories:
- name: WaterValve
17 changes: 14 additions & 3 deletions drivers/SmartThings/zigbee-valve/src/init.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
-- Copyright 2022 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

-- 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 ZigbeeDriver = require "st.zigbee"
local defaults = require "st.zigbee.defaults"
Expand All @@ -20,6 +30,7 @@ local function device_added(self, device)
end

local zigbee_valve_driver_template = {
health_check = false,
supported_capabilities = {
valve,
battery,
Expand Down
21 changes: 21 additions & 0 deletions drivers/SmartThings/zigbee-valve/src/sonoff/can_handle.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

local FINGERPRINTS = {
{ mfr = "SONOFF", model = "SWV-ZF2U" },
{ mfr = "SONOFF", model = "SWV-ZF2" }
}

local function sonoff_can_handle(opts, driver, device, ...)
for _, fingerprint in ipairs(FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
return true, require "sonoff"
end
end
if device.parent_device_id ~= nil then
return true, require "sonoff"
end
return false
end

return sonoff_can_handle
140 changes: 140 additions & 0 deletions drivers/SmartThings/zigbee-valve/src/sonoff/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

local capabilities = require "st.capabilities"
local log = require "log"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local Basic = zcl_clusters.Basic
local OnOff = zcl_clusters.OnOff
local PowerConfiguration = zcl_clusters.PowerConfiguration
local st_device = require "st.device"
local utils = require "st.utils"

local BATTERY_POLL_INTERVAL = 7200

local function find_child(parent, ep_id)
return parent:get_child_by_parent_assigned_key(string.format("%02X", ep_id))
end

--- OnOff Property Reporting → Valve Capability Point Event
local function onoff_attr_handler(driver, device, value, zb_rx)
local ep = zb_rx.address_header.src_endpoint.value
local target = device
if ep == 0x02 then
local child = find_child(device, 2)
if child then target = child end
end
log.info(string.format("[SWV] OnOff report ep=%s value=%s", tostring(ep), tostring(value.value)))
if value.value == true or value.value == 1 then
target:emit_event(capabilities.valve.valve.open())
else
target:emit_event(capabilities.valve.valve.closed())
end
end

--- Battery Percentage Attribute Handler
local function battery_percentage_handler(driver, device, value)
device:emit_event(capabilities.battery.battery(utils.round(value.value / 2)))
end

--- Lifecycle initialization handler
local function device_init(driver, device)
if device.network_type == st_device.NETWORK_TYPE_ZIGBEE then
device:set_find_child(find_child)
device.thread:call_on_schedule(
BATTERY_POLL_INTERVAL,
function()
device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device))
end
)
end
end

--- doConfigure
local function do_configure(driver, device)
device:try_update_metadata({ provisioning_state = "PROVISIONED" })
if device.network_type == st_device.NETWORK_TYPE_ZIGBEE then
device:emit_event(capabilities.valve.valve.closed())
device:emit_event(capabilities.battery.battery(100))
else
device:emit_event(capabilities.valve.valve.closed())
end
end

--- added
local function device_added(driver, device)
if device.network_type == st_device.NETWORK_TYPE_ZIGBEE then
if find_child(device, 2) == nil then
driver:try_create_device({
type = "EDGE_CHILD",
label = string.format("%s 2", device.label),
profile = "valve",
parent_device_id = device.id,
parent_assigned_child_key = string.format("%02X", 2),
vendor_provided_label = string.format("%s 2", device.label),
})
end
else
device:emit_event(capabilities.valve.valve.closed())
end
end

--- driverSwitched
local function driver_switched(driver, device)
device_added(driver, device)
end

--- valve.open
local function valve_open_handler(driver, device, command)
device:send(OnOff.server.commands.On(device))
device:send(OnOff.attributes.OnOff:read(device))
end

--- valve.close
local function valve_close_handler(driver, device, command)
device:send(OnOff.server.commands.Off(device))
device:send(OnOff.attributes.OnOff:read(device))
end

--- Identity cluster handler:button pressed on the device, sync valve states
local function identify_handler(driver, device, zb_rx)
log.info("[SWV] Identify received, syncing valve states")
device.thread:call_with_delay(2, function()
device:send(OnOff.attributes.OnOff:read(device))
device:send(OnOff.attributes.OnOff:read(device):to_endpoint(0x02))
end)
end

local sonoff_valve_handler = {
NAME = "SONOFF Water Valve Handler",
lifecycle_handlers = {
init = device_init,
added = device_added,
doConfigure = do_configure,
driverSwitched = driver_switched,
},
capability_handlers = {
[capabilities.valve.ID] = {
[capabilities.valve.commands.open.NAME] = valve_open_handler,
[capabilities.valve.commands.close.NAME] = valve_close_handler,
}
},
zigbee_handlers = {
attr = {
[OnOff.ID] = {
[OnOff.attributes.OnOff.ID] = onoff_attr_handler
},
[PowerConfiguration.ID] = {
[PowerConfiguration.attributes.BatteryPercentageRemaining.ID] = battery_percentage_handler
}
},
cluster = {
[Basic.ID] = {
[Basic.server.commands.ResetToFactoryDefaults.ID] = identify_handler
}
}
},
can_handle = require "sonoff.can_handle",
}

return sonoff_valve_handler
1 change: 1 addition & 0 deletions drivers/SmartThings/zigbee-valve/src/sub_drivers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ local lazy_load_if_possible = require "lazy_load_subdriver"
local sub_drivers = {
lazy_load_if_possible("sinope"),
lazy_load_if_possible("ezex"),
lazy_load_if_possible("sonoff"),
}
return sub_drivers
Loading