@@ -18,6 +18,7 @@ local clusters = require "st.matter.clusters"
1818local im = require " st.matter.interaction_model"
1919local utils = require " st.utils"
2020local lock_utils = require " lock_utils"
21+ local security = require " st.security"
2122
2223local version = require " version"
2324if version .api < 10 then
2627
2728local DoorLock = clusters .DoorLock
2829local PowerSource = clusters .PowerSource
30+ local DoorLockFeatureMapAttr = {ID = 0xFFFC , cluster = 0x0101 }
2931
3032local INITIAL_CREDENTIAL_INDEX = 1
3133local ALL_INDEX = 0xFFFE
3234local MIN_EPOCH_S = 0
3335local MAX_EPOCH_S = 0xffffffff
3436local THIRTY_YEARS_S = 946684800 -- 1970-01-01T00:00:00 ~ 2000-01-01T00:00:00
35-
36- local MODULAR_PROFILE_UPDATED = " __MODULAR_PROFILE_UPDATED"
37+ local PRIV_KEY_START = 15
38+ local PRIV_KEY_END = 78
39+ local PUB_KEY_START = 115
40+ local PUB_KEY_END = 242
41+ local PUB_KEY_PREFIX = " 04"
3742
3843local RESPONSE_STATUS_MAP = {
3944 [DoorLock .types .DlStatus .SUCCESS ] = " success" ,
@@ -194,6 +199,9 @@ local function device_init(driver, device)
194199 end
195200 end
196201 end
202+ if device .manufacturer_info .vendor_id == 0x135D then
203+ device :add_subscribed_attribute (DoorLockFeatureMapAttr )
204+ end
197205 device :subscribe ()
198206 end
199207
@@ -207,10 +215,118 @@ local function device_added(driver, device)
207215 end
208216end
209217
218+ -- This function check busy_state and if busy_state is false, set it to true(current time)
219+ local function is_busy_state_set (device )
220+ local c_time = os.time ()
221+ local busy_state = device :get_field (lock_utils .BUSY_STATE ) or false
222+ if busy_state == false or c_time - busy_state > 10 then
223+ device :set_field (lock_utils .BUSY_STATE , c_time , {persist = true })
224+ return false
225+ else
226+ return true
227+ end
228+ end
229+
230+ local function hex_string_to_octet_string (hex_string )
231+ if hex_string == nil then
232+ return nil
233+ end
234+ local octet_string = " "
235+ for i = 1 , # hex_string , 2 do
236+ local hex = hex_string :sub (i , i + 1 )
237+ octet_string = octet_string .. string.char (tonumber (hex , 16 ))
238+ end
239+ return octet_string
240+ end
241+
242+ local function create_group_id_resolving_key ()
243+ math.randomseed (os.time ())
244+ local result = string.format (" %02x" , math.random (0 , 255 ))
245+ for i = 1 , 15 do
246+ result = result .. string.format (" %02x" , math.random (0 , 255 ))
247+ end
248+ return result
249+ end
250+
251+ local function generate_keypair (device )
252+ local request_opts = {
253+ key_algorithm = {
254+ type = " ec" ,
255+ curve = " prime256v1"
256+ },
257+ signature_algorithm = " sha256" ,
258+ return_formats = {
259+ pem = true ,
260+ der = true
261+ },
262+ subject = {
263+ common_name = " reader config"
264+ },
265+ validity_days = 36500 ,
266+ x509_extensions = {
267+ key_usage = {
268+ critical = true ,
269+ digital_signature = true
270+ },
271+ certificate_policies = {
272+ critical = true ,
273+ policy_2030_5_self_signed_client = true
274+ }
275+ }
276+ }
277+ local status , result = security .generate_self_signed_cert (request_opts )
278+ local privKey = string.sub (utils .bytes_to_hex_string (status .key_der ), PRIV_KEY_START , PRIV_KEY_END )
279+ local pubKey = PUB_KEY_PREFIX .. string.sub (utils .bytes_to_hex_string (status .key_der ), PUB_KEY_START , PUB_KEY_END )
280+
281+ return privKey , pubKey
282+ end
283+
284+ local function set_reader_config (device )
285+ local cmdName = " setReaderConfig"
286+ local groupId = create_group_id_resolving_key ()
287+ local groupResolvingKey = nil
288+ local aliro_ble_uwb_eps = device :get_endpoints (DoorLock .ID , {feature_bitmap = DoorLock .types .Feature .ALIROBLEUWB })
289+ if # aliro_ble_uwb_eps > 0 then
290+ groupResolvingKey = create_group_id_resolving_key ()
291+ end
292+ local privKey , pubKey = generate_keypair (device )
293+
294+ -- Check busy state
295+ if is_busy_state_set (device ) then
296+ local command_result_info = {
297+ commandName = cmdName ,
298+ statusCode = " busy"
299+ }
300+ device :emit_event (capabilities .lockAliro .commandResult (
301+ command_result_info , {state_change = true , visibility = {displayed = false }}
302+ ))
303+ return
304+ end
305+
306+ -- Save values to field
307+ device :set_field (lock_utils .COMMAND_NAME , cmdName , {persist = true })
308+ device :set_field (lock_utils .VERIFICATION_KEY , pubKey , {persist = true })
309+ device :set_field (lock_utils .GROUP_ID , groupId , {persist = true })
310+ device :set_field (lock_utils .GROUP_RESOLVING_KEY , groupResolvingKey , {persist = true })
311+
312+ -- Send command
313+ local ep = find_default_endpoint (device , clusters .DoorLock .ID )
314+ device :send (
315+ DoorLock .server .commands .SetAliroReaderConfig (
316+ device , ep ,
317+ hex_string_to_octet_string (privKey ),
318+ hex_string_to_octet_string (pubKey ),
319+ hex_string_to_octet_string (groupId ),
320+ hex_string_to_octet_string (groupResolvingKey )
321+ )
322+ )
323+ end
324+
210325local function match_profile_modular (driver , device )
211326 local enabled_optional_component_capability_pairs = {}
212327 local main_component_capabilities = {}
213328 local modular_profile_name = " lock-modular"
329+ local is_support_aliro = false
214330 for _ , device_ep in pairs (device .endpoints ) do
215331 for _ , ep_cluster in pairs (device_ep .clusters ) do
216332 if ep_cluster .cluster_id == DoorLock .ID then
@@ -237,6 +353,7 @@ local function match_profile_modular(driver, device)
237353 end
238354 if clus_has_feature (DoorLock .types .Feature .ALIRO_PROVISIONING ) then
239355 table.insert (main_component_capabilities , capabilities .lockAliro .ID )
356+ is_support_aliro = true
240357 end
241358 break
242359 end
@@ -252,7 +369,12 @@ local function match_profile_modular(driver, device)
252369
253370 table.insert (enabled_optional_component_capability_pairs , {" main" , main_component_capabilities })
254371 device :try_update_metadata ({profile = modular_profile_name , optional_component_capabilities = enabled_optional_component_capability_pairs })
255- device :set_field (MODULAR_PROFILE_UPDATED , true )
372+ device :set_field (lock_utils .MODULAR_PROFILE_UPDATED , true )
373+
374+ local reader_config_updated = device :get_field (lock_utils .ALIRO_READER_CONFIG_UPDATED ) or nil
375+ if is_support_aliro == true and reader_config_updated ~= true then
376+ set_reader_config (device )
377+ end
256378end
257379
258380local function match_profile_switch (driver , device )
@@ -291,10 +413,10 @@ local function match_profile_switch(driver, device)
291413end
292414
293415local function info_changed (driver , device , event , args )
294- if device .profile .id == args .old_st_store .profile .id and not device :get_field (MODULAR_PROFILE_UPDATED ) then
416+ if device .profile .id == args .old_st_store .profile .id and not device :get_field (lock_utils . MODULAR_PROFILE_UPDATED ) then
295417 return
296418 end
297- device :set_field (MODULAR_PROFILE_UPDATED , nil )
419+ device :set_field (lock_utils . MODULAR_PROFILE_UPDATED , nil )
298420 for cap_id , attributes in pairs (subscribed_attributes ) do
299421 if device :supports_capability_by_id (cap_id ) then
300422 for _ , attr in ipairs (attributes ) do
@@ -341,18 +463,6 @@ local function driver_switched(driver, device)
341463 match_profile (driver , device )
342464end
343465
344- -- This function check busy_state and if busy_state is false, set it to true(current time)
345- local function is_busy_state_set (device )
346- local c_time = os.time ()
347- local busy_state = device :get_field (lock_utils .BUSY_STATE ) or false
348- if busy_state == false or c_time - busy_state > 10 then
349- device :set_field (lock_utils .BUSY_STATE , c_time , {persist = true })
350- return false
351- else
352- return true
353- end
354- end
355-
356466-- Matter Handler
357467---- ------------
358468-- Lock State --
@@ -526,21 +636,6 @@ local function max_year_schedule_of_user_handler(driver, device, ib, response)
526636 device :emit_event (capabilities .lockSchedules .yearDaySchedulesPerUser (ib .data .value , {visibility = {displayed = false }}))
527637end
528638
529- ---- ------------
530- -- Aliro Util --
531- ---- ------------
532- local function hex_string_to_octet_string (hex_string )
533- if hex_string == nil then
534- return nil
535- end
536- local octet_string = " "
537- for i = 1 , # hex_string , 2 do
538- local hex = hex_string :sub (i , i + 1 )
539- octet_string = octet_string .. string.char (tonumber (hex , 16 ))
540- end
541- return octet_string
542- end
543-
544639---- -------------------------------
545640-- Aliro Reader Verification Key --
546641---- -------------------------------
@@ -633,6 +728,23 @@ local function max_aliro_endpoint_key_handler(driver, device, ib, response)
633728 end
634729end
635730
731+ ---- -----------------------------------------
732+ -- Feature Map of Door Lock --
733+ ---- -----------------------------------------
734+ local function door_lock_feature_map_handler (driver , device , ib , response )
735+ for _ , device_ep in pairs (device .endpoints ) do
736+ for _ , ep_cluster in pairs (device_ep .clusters ) do
737+ if ep_cluster .cluster_id == DoorLock .ID then
738+ if ep_cluster .feature_map == ib .data .value then
739+ return
740+ end
741+ ep_cluster .feature_map = ib .data .value
742+ end
743+ end
744+ end
745+ match_profile (driver , device )
746+ end
747+
636748---- -----------------------------
637749-- Power Source Attribute List --
638750---- -----------------------------
@@ -2542,6 +2654,20 @@ local function handle_set_reader_config(driver, device, command)
25422654 return
25432655 end
25442656
2657+ local reader_config_updated = device :get_field (lock_utils .ALIRO_READER_CONFIG_UPDATED ) or nil
2658+ if reader_config_updated == true then
2659+ -- Update commandResult
2660+ local command_result_info = {
2661+ commandName = cmdName ,
2662+ statusCode = " success"
2663+ }
2664+ device :emit_event (capabilities .lockAliro .commandResult (
2665+ command_result_info , {state_change = true , visibility = {displayed = false }}
2666+ ))
2667+ device :set_field (lock_utils .BUSY_STATE , false , {persist = true })
2668+ return
2669+ end
2670+
25452671 -- Save values to field
25462672 device :set_field (lock_utils .COMMAND_NAME , cmdName , {persist = true })
25472673 device :set_field (lock_utils .VERIFICATION_KEY , verificationKey , {persist = true })
@@ -2564,43 +2690,12 @@ end
25642690local function set_aliro_reader_config_handler (driver , device , ib , response )
25652691 -- Get result
25662692 local cmdName = device :get_field (lock_utils .COMMAND_NAME )
2567- local verificationKey = device :get_field (lock_utils .VERIFICATION_KEY )
2568- local groupId = device :get_field (lock_utils .GROUP_ID )
2569- local groupResolvingKey = device :get_field (lock_utils .GROUP_RESOLVING_KEY )
2570-
2571- local status = " success"
2572- if ib .status == DoorLock .types .DlStatus .FAILURE then
2573- status = " failure"
2693+ local status = " failure"
2694+ if ib .status == DoorLock .types .DlStatus .SUCCESS then
2695+ status = " success"
2696+ device :set_field (lock_utils .ALIRO_READER_CONFIG_UPDATED , true )
25742697 elseif ib .status == DoorLock .types .DlStatus .INVALID_FIELD then
25752698 status = " invalidCommand"
2576- elseif ib .status == DoorLock .types .DlStatus .SUCCESS then
2577- if verificationKey ~= nil then
2578- device :emit_event (capabilities .lockAliro .readerVerificationKey (
2579- verificationKey ,
2580- {
2581- state_change = true ,
2582- visibility = {displayed = false }
2583- }
2584- ))
2585- end
2586- if groupId ~= nil then
2587- device :emit_event (capabilities .lockAliro .readerGroupIdentifier (
2588- groupId ,
2589- {
2590- state_change = true ,
2591- visibility = {displayed = false }
2592- }
2593- ))
2594- end
2595- if groupResolvingKey ~= nil then
2596- device :emit_event (capabilities .lockAliro .groupResolvingKey (
2597- groupResolvingKey ,
2598- {
2599- state_change = true ,
2600- visibility = {displayed = false }
2601- }
2602- ))
2603- end
26042699 end
26052700
26062701 -- Update commandResult
@@ -2896,6 +2991,7 @@ local new_matter_lock_handler = {
28962991 [DoorLock .attributes .AliroBLEAdvertisingVersion .ID ] = aliro_ble_advertising_version_handler ,
28972992 [DoorLock .attributes .NumberOfAliroCredentialIssuerKeysSupported .ID ] = max_aliro_credential_issuer_key_handler ,
28982993 [DoorLock .attributes .NumberOfAliroEndpointKeysSupported .ID ] = max_aliro_endpoint_key_handler ,
2994+ [DoorLockFeatureMapAttr .ID ] = door_lock_feature_map_handler ,
28992995 },
29002996 [PowerSource .ID ] = {
29012997 [PowerSource .attributes .AttributeList .ID ] = handle_power_source_attribute_list ,
0 commit comments