@@ -77,6 +77,17 @@ struct user_field_data {
77
77
uint8_t shift_right = 0 ;
78
78
};
79
79
80
+ // Used to create command frame
81
+ struct attribute_command_data {
82
+ // Attribute type that will be fetched from the base_node
83
+ attribute_store_type_t attribute_type;
84
+ // Attribute value state (reported, desired,...)
85
+ attribute_store_node_value_state_t attribute_state;
86
+ // If not ATTRIBUTE_STORE_INVALID_NODE, the function will not fetch attribute_type
87
+ // but will use this node directly
88
+ attribute_store_node_t node = ATTRIBUTE_STORE_INVALID_NODE;
89
+ };
90
+
80
91
// ///////////////////////////////////////////////////////////////////////////
81
92
// Type Helpers
82
93
// ///////////////////////////////////////////////////////////////////////////
@@ -311,6 +322,154 @@ sl_status_t
311
322
return status;
312
323
}
313
324
325
+ /* *
326
+ * @brief Create a command frame (SET or GET) based on the attribute store
327
+ *
328
+ * @param command Command to send (will be in frame[1], e.g USER_SET)
329
+ * @param command_data Attributes that will be in the frame (in order of appearance in the frame)
330
+ * @param base_node If not specified otherwise will fetch the attributes that are under this node
331
+ * @param frame Frame object from the callback
332
+ * @param frame_length Frame size from the callback
333
+ *
334
+ * @return sl_status_t SL_STATUS_OK if everything was fine
335
+ */
336
+ sl_status_t create_command_frame (uint8_t command,
337
+ std::vector<attribute_command_data> command_data,
338
+ attribute_store_node_t base_node,
339
+ uint8_t *frame,
340
+ uint16_t *frame_length)
341
+ {
342
+ frame[0 ] = COMMAND_CLASS_USER_CREDENTIAL;
343
+ frame[1 ] = command;
344
+
345
+ uint16_t current_index = 2 ;
346
+
347
+ for (auto &attribute_info: command_data) {
348
+ auto node_storage_type
349
+ = attribute_store_get_storage_type (attribute_info.attribute_type );
350
+ auto attribute_description
351
+ = attribute_store_get_type_name (attribute_info.attribute_type );
352
+
353
+ attribute_store_node_t node;
354
+ if (attribute_info.node == ATTRIBUTE_STORE_INVALID_NODE) {
355
+ node = attribute_store_get_first_child_by_type (
356
+ base_node,
357
+ attribute_info.attribute_type );
358
+ } else {
359
+ node = attribute_info.node ;
360
+ }
361
+
362
+ if (node == ATTRIBUTE_STORE_INVALID_NODE) {
363
+ sl_log_critical (LOG_TAG,
364
+ " Can't find node for Attribute %s" ,
365
+ attribute_description);
366
+ return SL_STATUS_FAIL;
367
+ }
368
+
369
+ sl_status_t status;
370
+ switch (node_storage_type) {
371
+ case U8_STORAGE_TYPE: {
372
+ uint8_t uint8_value;
373
+ status = attribute_store_read_value (node,
374
+ attribute_info.attribute_state ,
375
+ &uint8_value,
376
+ sizeof (uint8_value));
377
+ frame[current_index++] = uint8_value;
378
+ } break ;
379
+ case U16_STORAGE_TYPE: {
380
+ uint16_t uint16_value;
381
+ status = attribute_store_read_value (node,
382
+ attribute_info.attribute_state ,
383
+ &uint16_value,
384
+ sizeof (uint16_value));
385
+ auto exploded_uint16 = explode_uint16 (uint16_value);
386
+ frame[current_index++] = exploded_uint16.msb ;
387
+ frame[current_index++] = exploded_uint16.lsb ;
388
+ } break ;
389
+ // Variable length field
390
+ case BYTE_ARRAY_STORAGE_TYPE: {
391
+ // First get the length
392
+ auto credential_length_node = attribute_store_get_node_parent (node);
393
+
394
+ uint8_t credential_data_length = 0 ;
395
+ status = attribute_store_read_value (
396
+ credential_length_node,
397
+ attribute_info.attribute_state ,
398
+ &credential_data_length,
399
+ sizeof (credential_data_length));
400
+
401
+ if (status != SL_STATUS_OK) {
402
+ sl_log_error (
403
+ LOG_TAG,
404
+ " Missing BYTE_ARRAY_STORAGE_TYPE length for attribute %s" ,
405
+ attribute_description);
406
+ return SL_STATUS_NOT_SUPPORTED;
407
+ }
408
+
409
+ frame[current_index++] = credential_data_length;
410
+
411
+ // Then the data
412
+ std::vector<uint8_t > credential_data;
413
+ credential_data.resize (credential_data_length);
414
+ status = attribute_store_read_value (node,
415
+ attribute_info.attribute_state ,
416
+ credential_data.data (),
417
+ credential_data_length);
418
+
419
+ for (const uint8_t &cred: credential_data) {
420
+ frame[current_index++] = cred;
421
+ }
422
+
423
+ } break ;
424
+
425
+ case C_STRING_STORAGE_TYPE: {
426
+ char c_user_name[MAX_CHAR_SIZE];
427
+ // Unfortunately attribute_store_get_string is not exposed so we need to do this
428
+ switch (attribute_info.attribute_state ) {
429
+ case DESIRED_OR_REPORTED_ATTRIBUTE:
430
+ status
431
+ = attribute_store_get_desired_else_reported_string (node,
432
+ c_user_name,
433
+ MAX_CHAR_SIZE);
434
+ break ;
435
+ case DESIRED_ATTRIBUTE:
436
+ status = attribute_store_get_desired_string (node,
437
+ c_user_name,
438
+ MAX_CHAR_SIZE);
439
+ break ;
440
+ case REPORTED_ATTRIBUTE:
441
+ status = attribute_store_get_reported_string (node,
442
+ c_user_name,
443
+ MAX_CHAR_SIZE);
444
+ break ;
445
+ }
446
+
447
+ std::string user_name = c_user_name;
448
+ for (const char &c: user_name) {
449
+ frame[current_index++] = c;
450
+ }
451
+
452
+ } break ;
453
+ default :
454
+ sl_log_critical (LOG_TAG,
455
+ " Not supported type for %s" ,
456
+ attribute_description);
457
+ return SL_STATUS_FAIL;
458
+ }
459
+
460
+ if (status != SL_STATUS_OK) {
461
+ sl_log_error (LOG_TAG,
462
+ " Can't get value of Attribute %s" ,
463
+ attribute_description);
464
+ return SL_STATUS_NOT_SUPPORTED;
465
+ }
466
+ }
467
+
468
+ *frame_length = current_index;
469
+
470
+ return SL_STATUS_OK;
471
+ }
472
+
314
473
// ///////////////////////////////////////////////////////////////////////////
315
474
// Version & Attribute Creation
316
475
// ///////////////////////////////////////////////////////////////////////////
@@ -623,7 +782,7 @@ sl_status_t zwave_command_class_user_credential_all_user_checksum_handle_report(
623
782
}
624
783
625
784
// ///////////////////////////////////////////////////////////////////////////
626
- // Credential Get/Report
785
+ // Credential Set/ Get/Report
627
786
// ///////////////////////////////////////////////////////////////////////////
628
787
629
788
// Start credential interview process by starting with 0,0
@@ -669,6 +828,93 @@ void trigger_get_credential(attribute_store_node_t user_unique_id_node,
669
828
}
670
829
}
671
830
831
+ static sl_status_t zwave_command_class_user_credential_credential_set (
832
+ attribute_store_node_t credential_operation_type_node,
833
+ uint8_t *frame,
834
+ uint16_t *frame_length)
835
+ {
836
+ // Identifiers nodes
837
+ attribute_store_node_t credential_slot_node
838
+ = attribute_store_get_first_parent_with_type (credential_operation_type_node,
839
+ ATTRIBUTE (CREDENTIAL_SLOT));
840
+ attribute_store_node_t credential_type_node
841
+ = attribute_store_get_first_parent_with_type (credential_slot_node,
842
+ ATTRIBUTE (CREDENTIAL_TYPE));
843
+ attribute_store_node_t user_unique_id_node
844
+ = attribute_store_get_first_parent_with_type (credential_type_node,
845
+ ATTRIBUTE (USER_UNIQUE_ID));
846
+ // Since CREDENTIAL_DATA is not directly under credential_slot_node we need to fetch it first
847
+ attribute_store_node_t credential_length_node
848
+ = attribute_store_get_first_child_by_type (
849
+ credential_slot_node,
850
+ ATTRIBUTE (CREDENTIAL_DATA_LENGTH));
851
+ attribute_store_node_t credential_node
852
+ = attribute_store_get_first_child_by_type (credential_length_node,
853
+ ATTRIBUTE (CREDENTIAL_DATA));
854
+ // Get operation type
855
+ user_credential_operation_type_t operation_type = 0 ;
856
+ sl_status_t status
857
+ = attribute_store_get_desired (credential_operation_type_node,
858
+ &operation_type,
859
+ sizeof (operation_type));
860
+
861
+ if (status != SL_STATUS_OK) {
862
+ sl_log_error (LOG_TAG,
863
+ " Can't get operation type. Not sending CREDENTIAL_SET." );
864
+ return SL_STATUS_NOT_SUPPORTED;
865
+ }
866
+
867
+ sl_log_debug (LOG_TAG,
868
+ " Credential SET for Credential Slot %d, Credential Type %d, "
869
+ " User %d (operation type : %d)" ,
870
+ static_cast <user_credential_slot_t >(
871
+ attribute_store_get_reported_number (credential_slot_node)),
872
+ static_cast <user_credential_type_t >(
873
+ attribute_store_get_reported_number (credential_type_node)),
874
+ static_cast <user_credential_user_unique_id_t >(
875
+ attribute_store_get_reported_number (user_unique_id_node)),
876
+ operation_type);
877
+
878
+ // Since the data is not linear we provide the node directly
879
+ std::vector<attribute_command_data> set_data
880
+ = {{ATTRIBUTE (USER_UNIQUE_ID),
881
+ DESIRED_OR_REPORTED_ATTRIBUTE,
882
+ user_unique_id_node},
883
+ {ATTRIBUTE (CREDENTIAL_TYPE),
884
+ DESIRED_OR_REPORTED_ATTRIBUTE,
885
+ credential_type_node},
886
+ {ATTRIBUTE (CREDENTIAL_SLOT),
887
+ DESIRED_OR_REPORTED_ATTRIBUTE,
888
+ credential_slot_node},
889
+ {ATTRIBUTE (CREDENTIAL_OPERATION_TYPE),
890
+ DESIRED_ATTRIBUTE,
891
+ credential_operation_type_node}};
892
+
893
+ // Add the credential data if we are not trying to remove a credential
894
+ if (operation_type != USER_CREDENTIAL_OPERATION_TYPE_DELETE) {
895
+ set_data.push_back ({ATTRIBUTE (CREDENTIAL_DATA),
896
+ DESIRED_OR_REPORTED_ATTRIBUTE,
897
+ credential_node});
898
+ }
899
+ status = create_command_frame (CREDENTIAL_SET,
900
+ set_data,
901
+ credential_slot_node,
902
+ frame,
903
+ frame_length);
904
+
905
+ if (status != SL_STATUS_OK) {
906
+ sl_log_error (LOG_TAG, " Can't create Credential SET frame" );
907
+ return SL_STATUS_NOT_SUPPORTED;
908
+ }
909
+ // If we are deleting the credential we are setting 0x00 as a credential length
910
+ if (operation_type == USER_CREDENTIAL_OPERATION_TYPE_DELETE) {
911
+ frame[*frame_length] = 0x00 ;
912
+ *frame_length += 1 ;
913
+ }
914
+
915
+ return SL_STATUS_OK;
916
+ }
917
+
672
918
/* *
673
919
* @brief Credential GET
674
920
*
@@ -1328,6 +1574,11 @@ sl_status_t zwave_command_class_user_credential_init()
1328
1574
NULL ,
1329
1575
&zwave_command_class_user_credential_credential_get);
1330
1576
1577
+ attribute_resolver_register_rule (
1578
+ ATTRIBUTE (CREDENTIAL_OPERATION_TYPE),
1579
+ &zwave_command_class_user_credential_credential_set,
1580
+ NULL );
1581
+
1331
1582
// https://github.com/Z-Wave-Alliance/AWG/pull/124#discussion_r1484473752
1332
1583
// Discussion about delaying the user interview process after the inclusion
1333
1584
0 commit comments