-
Notifications
You must be signed in to change notification settings - Fork 556
[WWSTCERT-13446, WWSTCERT-13450, WWSTCERT-13454, WWSTCERT-13458] SONOFF/SWV1C #3157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d3ca759
244e0ee
539f602
caee9a2
b938a79
3270a93
30cf526
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| name: valve-battery | ||
| components: | ||
| - id: main | ||
| capabilities: | ||
| - id: valve | ||
| version: 1 | ||
| - id: battery | ||
| version: 1 | ||
| - id: firmwareUpdate | ||
| version: 1 | ||
| - id: refresh | ||
| version: 1 | ||
| categories : | ||
| - name: WaterValve |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| -- Copyright 2026 SmartThings, Inc. | ||
| -- Licensed under the Apache License, Version 2.0 | ||
|
|
||
| local FINGERPRINTS = { | ||
| { mfr = "SONOFF", model = "SWV-ZFU" }, | ||
| { mfr = "SONOFF", model = "SWV-ZFE" }, | ||
| { mfr = "SONOFF", model = "SWV-ZNU" }, | ||
| { mfr = "SONOFF", model = "SWV-ZNE" }, | ||
| } | ||
|
|
||
| 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 | ||
| return false | ||
| end | ||
|
|
||
| return sonoff_can_handle |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,96 @@ | ||||
| -- Copyright 2026 SmartThings, Inc. | ||||
| -- Licensed under the Apache License, Version 2.0 | ||||
|
|
||||
| local capabilities = require "st.capabilities" | ||||
| local zcl_clusters = require "st.zigbee.zcl.clusters" | ||||
| local OnOff = zcl_clusters.OnOff | ||||
| local PowerConfiguration = zcl_clusters.PowerConfiguration | ||||
| local utils = require "st.utils" | ||||
|
|
||||
| -- Battery Polling Interval (seconds): SWV1C is a battery sleep device, polling every 2 hours | ||||
| local BATTERY_POLL_INTERVAL = 7200 | ||||
|
|
||||
| --- OnOff Property Reporting Handler → Valve Capability Point Event | ||||
| --- It must be handled explicitly because the child driver defines capability_handlers. | ||||
| --- The default OnOff→valve mapping from the parent driver will be skipped (only OnOff→switch remains) | ||||
| --- @param driver table Driver instance | ||||
| --- @param device table Device instance | ||||
| --- @param value table Zigbee attribute value | ||||
| local function onoff_attr_handler(driver, device, value) | ||||
| local is_on = value.value ~= false and value.value ~= 0 | ||||
| if is_on then | ||||
| device:emit_event(capabilities.valve.valve.open()) | ||||
| else | ||||
| device:emit_event(capabilities.valve.valve.closed()) | ||||
| end | ||||
| end | ||||
|
|
||||
| --- Battery Percentage Attribute Handler | ||||
| --- Zigbee BatteryPercentageRemaining range 0-200 (0%-100%), needs to be divided by 2 | ||||
| --- @param driver table Driver instance | ||||
| --- @param device table Device instance | ||||
| --- @param value table Zigbee attribute value | ||||
| local function battery_percentage_handler(driver, device, value) | ||||
| local raw = value.value | ||||
| local percent = utils.round(raw / 2) | ||||
| device:emit_event(capabilities.battery.battery(percent)) | ||||
| end | ||||
|
|
||||
| --- Lifecycle initialization handler | ||||
| --- Ensures battery data is available by periodically actively reading BatteryPercentageRemaining | ||||
| --- @param driver table Driver instance | ||||
| --- @param device table Device instance | ||||
| local function device_init(driver, device) | ||||
| device.thread:call_on_schedule( | ||||
| BATTERY_POLL_INTERVAL, | ||||
| function() | ||||
| device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device)) | ||||
| end | ||||
| ) | ||||
| end | ||||
|
Comment on lines
+43
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yanggx24 Don't You want to configure PowerConfiguration (instead of polling) and other clusters (for reporting) ? Here is the example of how to do it in the subdriver: SmartThingsEdgeDrivers/drivers/SmartThings/zigbee-power-meter/src/frient/EMIZB-151/init.lua Line 98 in 71bbd3d
|
||||
|
|
||||
| --- valve.open ability handler | ||||
| --- @param driver table driver instance | ||||
| --- @param device table device instance | ||||
| --- @param command table ability command | ||||
| 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 capability handler | ||||
| --- @param driver table driver instance | ||||
| --- @param device table device instance | ||||
| --- @param command table capability command | ||||
| local function valve_close_handler(driver, device, command) | ||||
| device:send(OnOff.server.commands.Off(device)) | ||||
| device:send(OnOff.attributes.OnOff:read(device)) | ||||
| end | ||||
|
|
||||
| local sonoff_valve_handler = { | ||||
| NAME = "SONOFF Water Valve Handler", | ||||
| lifecycle_handlers = { | ||||
| init = device_init | ||||
| }, | ||||
| capability_handlers = { | ||||
| [capabilities.valve.ID] = { | ||||
| [capabilities.valve.commands.open.NAME] = valve_open_handler, | ||||
| [capabilities.valve.commands.close.NAME] = valve_close_handler, | ||||
| } | ||||
| }, | ||||
|
KKlimczukS marked this conversation as resolved.
|
||||
| zigbee_handlers = { | ||||
| attr = { | ||||
| -- OnOff property reporting → valve event (to compensate for the defect of the parent driver's default mapping being skipped) | ||||
| [OnOff.ID] = { | ||||
| [OnOff.attributes.OnOff.ID] = onoff_attr_handler | ||||
| }, | ||||
|
KKlimczukS marked this conversation as resolved.
|
||||
| -- Battery percentage attribute reporting → battery event | ||||
| [PowerConfiguration.ID] = { | ||||
| [PowerConfiguration.attributes.BatteryPercentageRemaining.ID] = battery_percentage_handler | ||||
| } | ||||
| } | ||||
| }, | ||||
| can_handle = require "sonoff.can_handle", | ||||
| } | ||||
|
|
||||
| return sonoff_valve_handler | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| -- Copyright 2026 SmartThings, Inc. | ||
| -- Licensed under the Apache License, Version 2.0 | ||
|
|
||
| local function load_helper(api, lazy_loader, legacy_module) | ||
| package.loaded["version"] = { api = api } | ||
| package.loaded["st.zigbee"] = { | ||
| lazy_load_sub_driver = function(name) | ||
| assert(name == legacy_module) | ||
| return lazy_loader | ||
| end, | ||
| lazy_load_sub_driver_v2 = function(name) | ||
| assert(name == legacy_module) | ||
| return lazy_loader | ||
| end, | ||
| } | ||
| package.preload[legacy_module] = function() | ||
| return legacy_module | ||
| end | ||
|
|
||
| return dofile("lazy_load_subdriver.lua") | ||
| end | ||
|
|
||
| local legacy_module = "test-sub-driver" | ||
| local marker = {} | ||
|
|
||
| print('Running test "lazy loader supports all API branches"') | ||
|
|
||
| local helper = load_helper(16, marker, legacy_module) | ||
| assert(helper(legacy_module) == marker) | ||
|
|
||
| helper = load_helper(10, marker, legacy_module) | ||
| assert(helper(legacy_module) == marker) | ||
|
|
||
| helper = load_helper(8, marker, legacy_module) | ||
| assert(helper(legacy_module) == legacy_module) | ||
|
|
||
| package.preload[legacy_module] = nil | ||
| package.loaded["version"] = nil | ||
| package.loaded["st.zigbee"] = nil | ||
|
|
||
| print("PASSED") | ||
| print("Passed 1 of 1 tests") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yanggx24 no need to change it. Please keep it as it was.