|
39 | 39 | -- [[ SYNC STATE MANAGEMENT ]] -- |
40 | 40 |
|
41 | 41 | function lock_utils.sync_device_state(device) |
| 42 | + local capabilities = require "st.capabilities" |
42 | 43 | local clusters = require "st.zigbee.zcl.clusters" |
| 44 | + |
| 45 | + -- skip if unsupported, already synced, or busy (including a sync already in progress). is_device_busy |
| 46 | + -- expires stale busy state, so a sync that stalled on a lost response can be retried. |
| 47 | + if not (device:supports_capability(capabilities.lockCredentials) and |
| 48 | + device:get_field(consts.SYNC.CODES_SYNCED) ~= consts.SYNC.CODES_SYNC_VERSION and |
| 49 | + not lock_utils.is_device_busy(device)) then |
| 50 | + return |
| 51 | + end |
| 52 | + |
| 53 | + -- a persisted index from a sync begun under a different sync version is stale; start over. |
| 54 | + if device:get_field(consts.SYNC.CODE_INDEX_VERSION) ~= consts.SYNC.CODES_SYNC_VERSION then |
| 55 | + device:set_field(consts.SYNC.CODE_INDEX, nil, { persist = true }) |
| 56 | + end |
| 57 | + |
43 | 58 | -- Per spec, this attribute should be a boolean set to True if it is ok for the door lock server to send PINs over the air. |
44 | 59 | device:send(clusters.DoorLock.attributes.SendPINOverTheAir:write(device, true)) |
45 | 60 |
|
46 | 61 | -- if consts.SYNC.CODE_INDEX is nil, we haven't started syncing codes from the lock yet, |
47 | | - -- so start the process from 1. note: the Zigbee "Master Code" is associated with index 0. |
| 62 | + -- so start the process from 0. Persist the index so we can resume if the driver is restarted mid-sync. |
48 | 63 | if (device:get_field(consts.SYNC.CODE_INDEX) == nil) then |
49 | | - device:set_field(consts.SYNC.CODE_INDEX, 1) |
| 64 | + device:set_field(consts.SYNC.CODE_INDEX, 0, { persist = true }) |
| 65 | + device:set_field(consts.SYNC.CODE_INDEX_VERSION, consts.SYNC.CODES_SYNC_VERSION, { persist = true }) |
50 | 66 | end |
51 | 67 | lock_utils.set_busy_state(device, consts.SYNC.CODES_FROM_LOCK) |
52 | 68 | device:send(clusters.DoorLock.server.commands.GetPINCode(device, device:get_field(consts.SYNC.CODE_INDEX))) |
53 | 69 | end |
54 | 70 |
|
| 71 | +-- Advance the sync-codes-from-lock flow after a response for the current code index (consts.SYNC.CODE_INDEX). |
| 72 | +-- `occupied` is true only when the lock reported an occupied slot at that index. |
| 73 | +-- `default_response` is true when the step was resolved by a Default Response: that is not treated as a |
| 74 | +-- failure, but it is not an occupied slot either, so it does not count toward the consecutive unoccupied codes. |
| 75 | +function lock_utils.continue_code_sync(device, occupied, default_response) |
| 76 | + local clusters = require "st.zigbee.zcl.clusters" |
| 77 | + local code_index = device:get_field(consts.SYNC.CODE_INDEX) |
| 78 | + if code_index == nil then return end |
| 79 | + |
| 80 | + if occupied then |
| 81 | + lock_utils.add_reported_credential(device, code_index) |
| 82 | + -- reset the consecutive unoccupied codes counter since we found an occupied code |
| 83 | + device:set_field(consts.SYNC.CONSECUTIVE_UNOCCUPIED_CODES, 0) |
| 84 | + elseif not default_response then |
| 85 | + device:set_field(consts.SYNC.CONSECUTIVE_UNOCCUPIED_CODES, (device:get_field(consts.SYNC.CONSECUTIVE_UNOCCUPIED_CODES) or 0) + 1) |
| 86 | + end |
| 87 | + |
| 88 | + if code_index >= tables.get_max_entries(device, "credentials") or |
| 89 | + (device:get_field(consts.SYNC.CONSECUTIVE_UNOCCUPIED_CODES) or 0) > 5 then |
| 90 | + -- stop if we hit the max number of codes supported by the lock, or if we have received 5 consecutive unoccupied codes. |
| 91 | + device:set_field(consts.SYNC.CODE_INDEX, nil, { persist = true }) |
| 92 | + device:set_field(consts.SYNC.CONSECUTIVE_UNOCCUPIED_CODES, nil) |
| 93 | + device:set_field(consts.SYNC.RETRIED_CODE_INDEX, nil) |
| 94 | + device:set_field(consts.SYNC.CODES_SYNCED, consts.SYNC.CODES_SYNC_VERSION, { persist = true }) |
| 95 | + lock_utils.clear_busy_state(device) |
| 96 | + else |
| 97 | + local next_index = code_index + 1 |
| 98 | + device:set_field(consts.SYNC.CODE_INDEX, next_index, { persist = true }) |
| 99 | + lock_utils.set_busy_state(device, consts.SYNC.CODES_FROM_LOCK) |
| 100 | + device:send(clusters.DoorLock.server.commands.GetPINCode(device, next_index)) |
| 101 | + end |
| 102 | +end |
| 103 | + |
55 | 104 |
|
56 | 105 | -- [[ CREDENTIAL TABLE MANAGEMENT ]] -- |
57 | 106 |
|
|
117 | 166 | -- is left in place |
118 | 167 | function lock_utils.add_reported_credential(device, credential_index) |
119 | 168 | if tables.find_entry(device, "credentials", credential_index) then return end |
120 | | - lock_utils.add_credential_and_user(device, { |
| 169 | + return lock_utils.add_credential_and_user(device, { |
121 | 170 | userIndex = tables.next_index(device, "users"), |
122 | 171 | credentialIndex = credential_index, |
123 | 172 | }, true) |
|
0 commit comments