Skip to content

Commit 910ba4f

Browse files
committed
CHAD-16364: Update Z-Wave lock capabilities
1 parent 0bb81cb commit 910ba4f

40 files changed

Lines changed: 3235 additions & 551 deletions

drivers/SmartThings/zwave-lock/profiles/base-lock-tamper.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ components:
66
version: 1
77
- id: lockCodes
88
version: 1
9+
- id: lockCredentials
10+
version: 1
11+
- id: lockUsers
12+
version: 1
913
- id: battery
1014
version: 1
1115
- id: tamperAlert

drivers/SmartThings/zwave-lock/profiles/base-lock.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ components:
66
version: 1
77
- id: lockCodes
88
version: 1
9+
- id: lockCredentials
10+
version: 1
11+
- id: lockUsers
12+
version: 1
913
- id: battery
1014
version: 1
1115
- id: refresh

drivers/SmartThings/zwave-lock/src/apiv6_bugfix/init.lua

Lines changed: 0 additions & 26 deletions
This file was deleted.

drivers/SmartThings/zwave-lock/src/init.lua

Lines changed: 39 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
1-
-- Copyright 2022 SmartThings
2-
--
3-
-- Licensed under the Apache License, Version 2.0 (the "License");
4-
-- you may not use this file except in compliance with the License.
5-
-- You may obtain a copy of the License at
6-
--
7-
-- http://www.apache.org/licenses/LICENSE-2.0
8-
--
9-
-- Unless required by applicable law or agreed to in writing, software
10-
-- distributed under the License is distributed on an "AS IS" BASIS,
11-
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12-
-- See the License for the specific language governing permissions and
13-
-- limitations under the License.
1+
-- Copyright © 2022 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
143

154
local capabilities = require "st.capabilities"
165
--- @type st.zwave.CommandClass
@@ -19,21 +8,15 @@ local cc = require "st.zwave.CommandClass"
198
local ZwaveDriver = require "st.zwave.driver"
209
--- @type st.zwave.defaults
2110
local defaults = require "st.zwave.defaults"
22-
--- @type st.zwave.CommandClass.DoorLock
23-
local DoorLock = (require "st.zwave.CommandClass.DoorLock")({ version = 1 })
24-
--- @type st.zwave.CommandClass.UserCode
25-
local UserCode = (require "st.zwave.CommandClass.UserCode")({ version = 1 })
26-
--- @type st.zwave.CommandClass.Battery
27-
local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 })
28-
--- @type st.zwave.CommandClass.Time
29-
local Time = (require "st.zwave.CommandClass.Time")({ version = 1 })
30-
local constants = require "st.zwave.constants"
31-
local utils = require "st.utils"
32-
local json = require "st.json"
11+
12+
local do_refresh = function(self, device)
13+
local DoorLock = (require "st.zwave.CommandClass.DoorLock")({ version = 1 })
14+
local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 })
15+
device:send(DoorLock:OperationGet({}))
16+
device:send(Battery:Get({}))
17+
end
3318

3419
local SCAN_CODES_CHECK_INTERVAL = 30
35-
local MIGRATION_COMPLETE = "migrationComplete"
36-
local MIGRATION_RELOAD_SKIPPED = "migrationReloadSkipped"
3720

3821
local function periodic_codes_state_verification(driver, device)
3922
local scan_codes_state = device:get_latest_state("main", capabilities.lockCodes.ID, capabilities.lockCodes.scanCodes.NAME)
@@ -53,100 +36,38 @@ local function periodic_codes_state_verification(driver, device)
5336
end
5437
end
5538

56-
local function populate_state_from_data(device)
57-
if device.data.lockCodes ~= nil and device:get_field(MIGRATION_COMPLETE) ~= true then
58-
-- build the lockCodes table
59-
local lockCodes = {}
60-
local lc_data = json.decode(device.data.lockCodes)
61-
for k, v in pairs(lc_data) do
62-
lockCodes[k] = v
63-
end
64-
-- Populate the devices `lockCodes` field
65-
device:set_field(constants.LOCK_CODES, utils.deep_copy(lockCodes), { persist = true })
66-
-- Populate the devices state history cache
67-
device.state_cache["main"] = device.state_cache["main"] or {}
68-
device.state_cache["main"][capabilities.lockCodes.ID] = device.state_cache["main"][capabilities.lockCodes.ID] or {}
69-
device.state_cache["main"][capabilities.lockCodes.ID][capabilities.lockCodes.lockCodes.NAME] = {value = json.encode(utils.deep_copy(lockCodes))}
70-
71-
device:set_field(MIGRATION_COMPLETE, true, { persist = true })
72-
end
73-
end
74-
75-
--- Builds up initial state for the device
76-
---
77-
--- @param self st.zwave.Driver
78-
--- @param device st.zwave.Device
79-
local function added_handler(self, device)
80-
populate_state_from_data(device)
81-
if device.data.lockCodes == nil or device:get_field(MIGRATION_RELOAD_SKIPPED) == true then
82-
if (device:supports_capability(capabilities.lockCodes)) then
83-
self:inject_capability_command(device,
84-
{ capability = capabilities.lockCodes.ID,
85-
command = capabilities.lockCodes.commands.reloadAllCodes.NAME,
86-
args = {} })
87-
device.thread:call_with_delay(
88-
SCAN_CODES_CHECK_INTERVAL,
89-
function(d)
90-
periodic_codes_state_verification(self, device)
91-
end
92-
)
39+
local do_added = function(driver, device)
40+
-- this variable should only be present for test cases trying to test the old capabilities.
41+
if device.useOldCapabilityForTesting == true then
42+
-- added handler from using old capabilities
43+
driver:inject_capability_command(device,
44+
{ capability = capabilities.lockCodes.ID,
45+
command = capabilities.lockCodes.commands.reloadAllCodes.NAME,
46+
args = {} })
47+
device.thread:call_with_delay(
48+
SCAN_CODES_CHECK_INTERVAL,
49+
function()
50+
periodic_codes_state_verification(driver, device)
51+
end
52+
)
53+
local DoorLock = (require "st.zwave.CommandClass.DoorLock")({ version = 1 })
54+
local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 })
55+
device:send(DoorLock:OperationGet({}))
56+
device:send(Battery:Get({}))
57+
if (device:supports_capability(capabilities.tamperAlert)) then
58+
device:emit_event(capabilities.tamperAlert.tamper.clear())
9359
end
9460
else
95-
device:set_field(MIGRATION_RELOAD_SKIPPED, true, { persist = true })
96-
end
97-
device:send(DoorLock:OperationGet({}))
98-
device:send(Battery:Get({}))
99-
if (device:supports_capability(capabilities.tamperAlert)) then
100-
device:emit_event(capabilities.tamperAlert.tamper.clear())
101-
end
102-
end
103-
104-
local init_handler = function(driver, device, event)
105-
populate_state_from_data(device)
106-
-- temp fix before this can be changed from being persisted in memory
107-
device:set_field(constants.CODE_STATE, nil, { persist = true })
108-
end
109-
110-
local do_refresh = function(self, device)
111-
device:send(DoorLock:OperationGet({}))
112-
device:send(Battery:Get({}))
113-
end
114-
115-
--- @param driver st.zwave.Driver
116-
--- @param device st.zwave.Device
117-
--- @param cmd table
118-
local function update_codes(driver, device, cmd)
119-
local delay = 0
120-
-- args.codes is json
121-
for name, code in pairs(cmd.args.codes) do
122-
-- these seem to come in the format "code[slot#]: code"
123-
local code_slot = tonumber(string.gsub(name, "code", ""), 10)
124-
if (code_slot ~= nil) then
125-
if (code ~= nil and (code ~= "0" and code ~= "")) then
126-
-- code changed
127-
device.thread:call_with_delay(delay, function ()
128-
device:send(UserCode:Set({
129-
user_identifier = code_slot,
130-
user_code = code,
131-
user_id_status = UserCode.user_id_status.ENABLED_GRANT_ACCESS}))
132-
end)
133-
delay = delay + 2.2
134-
else
135-
-- code deleted
136-
device.thread:call_with_delay(delay, function ()
137-
device:send(UserCode:Set({user_identifier = code_slot, user_id_status = UserCode.user_id_status.AVAILABLE}))
138-
end)
139-
delay = delay + 2.2
140-
device.thread:call_with_delay(delay, function ()
141-
device:send(UserCode:Get({user_identifier = code_slot}))
142-
end)
143-
delay = delay + 2.2
144-
end
61+
if device:supports_capability_by_id(capabilities.lockCodes.ID) then
62+
device:emit_event(capabilities.lockCodes.migrated(true, { visibility = { displayed = false } }))
63+
-- make the driver call this command again, it will now be handled in new capabilities.
64+
driver.lifecycle_dispatcher:dispatch(driver, device, "added")
14565
end
14666
end
14767
end
14868

14969
local function time_get_handler(driver, device, cmd)
70+
local Time = (require "st.zwave.CommandClass.Time")({ version = 1 })
15071
local time = os.date("*t")
15172
device:send_to_component(
15273
Time:Report({
@@ -162,32 +83,26 @@ local driver_template = {
16283
supported_capabilities = {
16384
capabilities.lock,
16485
capabilities.lockCodes,
86+
capabilities.lockUsers,
87+
capabilities.lockCredentials,
16588
capabilities.battery,
16689
capabilities.tamperAlert
16790
},
16891
lifecycle_handlers = {
169-
added = added_handler,
170-
init = init_handler,
92+
added = do_added
17193
},
17294
capability_handlers = {
173-
[capabilities.lockCodes.ID] = {
174-
[capabilities.lockCodes.commands.updateCodes.NAME] = update_codes
175-
},
17695
[capabilities.refresh.ID] = {
17796
[capabilities.refresh.commands.refresh.NAME] = do_refresh
17897
}
17998
},
18099
zwave_handlers = {
181100
[cc.TIME] = {
182-
[Time.GET] = time_get_handler -- used by DanaLock
101+
[0x01] = time_get_handler -- used by DanaLock
183102
}
184103
},
185104
sub_drivers = {
186-
require("zwave-alarm-v1-lock"),
187-
require("schlage-lock"),
188-
require("samsung-lock"),
189-
require("keywe-lock"),
190-
require("apiv6_bugfix"),
105+
require("sub_drivers")
191106
}
192107
}
193108

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Copyright 2025 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
return function(sub_driver_name)
5+
-- gets the current lua libs api version
6+
local version = require "version"
7+
local ZwaveDriver = require "st.zwave.driver"
8+
if version.api >= 16 then
9+
return ZwaveDriver.lazy_load_sub_driver_v2(sub_driver_name)
10+
elseif version.api >= 9 then
11+
return ZwaveDriver.lazy_load_sub_driver(require(sub_driver_name))
12+
else
13+
return require(sub_driver_name)
14+
end
15+
end

0 commit comments

Comments
 (0)