@@ -77,6 +77,17 @@ struct user_field_data {
7777 uint8_t shift_right = 0 ;
7878};
7979
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+
8091// ///////////////////////////////////////////////////////////////////////////
8192// Type Helpers
8293// ///////////////////////////////////////////////////////////////////////////
@@ -311,6 +322,154 @@ sl_status_t
311322 return status;
312323}
313324
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+
314473// ///////////////////////////////////////////////////////////////////////////
315474// Version & Attribute Creation
316475// ///////////////////////////////////////////////////////////////////////////
@@ -623,7 +782,7 @@ sl_status_t zwave_command_class_user_credential_all_user_checksum_handle_report(
623782}
624783
625784// ///////////////////////////////////////////////////////////////////////////
626- // Credential Get/Report
785+ // Credential Set/ Get/Report
627786// ///////////////////////////////////////////////////////////////////////////
628787
629788// 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,
669828 }
670829}
671830
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+
672918/* *
673919 * @brief Credential GET
674920 *
@@ -1328,6 +1574,11 @@ sl_status_t zwave_command_class_user_credential_init()
13281574 NULL ,
13291575 &zwave_command_class_user_credential_credential_get);
13301576
1577+ attribute_resolver_register_rule (
1578+ ATTRIBUTE (CREDENTIAL_OPERATION_TYPE),
1579+ &zwave_command_class_user_credential_credential_set,
1580+ NULL );
1581+
13311582 // https://github.com/Z-Wave-Alliance/AWG/pull/124#discussion_r1484473752
13321583 // Discussion about delaying the user interview process after the inclusion
13331584
0 commit comments