@@ -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