text: // Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "esp_ble_mesh_networking_api.h"
  #include "ble_mesh_adapter.h"
  
  #if (CONFIG_BLE_MESH_CFG_CLI)
  typedef struct {
      struct arg_str *action_type;
      struct arg_str *set_state;
      struct arg_int *opcode;
      struct arg_int *unicast_address;
      struct arg_int *appkey_index;
      struct arg_int *mod_id;
      struct arg_int *addr;
      struct arg_int *cid;
      struct arg_int *value;
      struct arg_int *relay_statue;
      struct arg_int *relay_transmit;
      struct arg_int *net_idx;
      struct arg_end *end;
  } ble_mesh_client_get_set_state_t;
  ble_mesh_client_get_set_state_t configuration_client_model_operation;
  
  void ble_mesh_register_configuration_client_model_command(void);
  void ble_mesh_configuration_client_model_cb(esp_ble_mesh_cfg_client_cb_event_t event,
          esp_ble_mesh_cfg_client_cb_param_t *param);
  
  void ble_mesh_register_configuration_client_model(void)
  {
      ble_mesh_register_configuration_client_model_command();
  }
  
  void ble_mesh_configuration_client_model_cb(esp_ble_mesh_cfg_client_cb_event_t event,
          esp_ble_mesh_cfg_client_cb_param_t *param)
  {
      uint32_t opcode;
      ESP_LOGD(TAG, "enter %s, event = %x\n, error_code = %x\n", __func__, event, param->error_code);
  
      if (!param->error_code) {
          opcode = param->params->opcode;
          switch (event) {
          case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT:
              switch (opcode) {
              case ESP_BLE_MESH_MODEL_OP_BEACON_GET:
                  ESP_LOGI(TAG, "CfgClient:beacon,0x%x", param->status_cb.beacon_status.beacon);
                  break;
              case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET:
                  ESP_LOGI(TAG, "CfgClient:page,0x%x,len,0x%x", param->status_cb.comp_data_status.page, param->status_cb.comp_data_status.composition_data->len);
                  break;
              case ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_GET:
                  ESP_LOGI(TAG, "CfgClient:ttl,0x%x", param->status_cb.default_ttl_status.default_ttl);
                  break;
              case ESP_BLE_MESH_MODEL_OP_GATT_PROXY_GET:
                  ESP_LOGI(TAG, "CfgClient:proxy,0x%x", param->status_cb.gatt_proxy_status.gatt_proxy);
                  break;
              case ESP_BLE_MESH_MODEL_OP_RELAY_GET:
                  ESP_LOGI(TAG, "CfgClient:relay,0x%x,retransmit,0x%x", param->status_cb.relay_status.relay, param->status_cb.relay_status.retransmit);
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_GET:
                  if (param->status_cb.model_pub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:PublishGet,OK,0x%x", param->status_cb.model_pub_status.publish_addr);
                  } else {
                      ESP_LOGI(TAG, "CfgClient:PublishGet,Fail");
                  }
  
                  break;
              case ESP_BLE_MESH_MODEL_OP_FRIEND_GET:
                  ESP_LOGI(TAG, "CfgClient:friend,0x%x", param->status_cb.friend_status.friend_state);
                  break;
              case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_GET:
                  if (param->status_cb.heartbeat_pub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatPubGet,OK,destination:0x%x,countlog:0x%x,periodlog:0x%x,ttl:0x%x,features:0x%x,net_idx:0x%x",
                               param->status_cb.heartbeat_pub_status.dst, param->status_cb.heartbeat_pub_status.count,  param->status_cb.heartbeat_pub_status.period,
                               param->status_cb.heartbeat_pub_status.ttl, param->status_cb.heartbeat_pub_status.features, param->status_cb.heartbeat_pub_status.net_idx);
                  } else {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatGet,Fail,%d", param->status_cb.heartbeat_pub_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_GET:
                  if (param->status_cb.heartbeat_sub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatSubGet,OK,source:0x%x,destination:0x%x, periodlog:0x%x,countlog:0x%x,minhops:0x%x,maxhops:0x%x",
                               param->status_cb.heartbeat_sub_status.src, param->status_cb.heartbeat_sub_status.dst, param->status_cb.heartbeat_sub_status.period,
                               param->status_cb.heartbeat_sub_status.count, param->status_cb.heartbeat_sub_status.min_hops, param->status_cb.heartbeat_sub_status.max_hops);
                  } else {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatSubGet,Fail,%d", param->status_cb.heartbeat_sub_status.status);
                  }
                  break;
              default:
                  ESP_LOGI(TAG, "Not supported config client get message opcode");
                  break;
              }
              break;
          case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT:
              switch (opcode) {
              case ESP_BLE_MESH_MODEL_OP_BEACON_SET:
                  ESP_LOGI(TAG, "CfgClient:beacon,0x%x", param->status_cb.beacon_status.beacon);
                  break;
              case ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_SET:
                  ESP_LOGI(TAG, "CfgClient:ttl,0x%x", param->status_cb.default_ttl_status.default_ttl);
                  break;
              case ESP_BLE_MESH_MODEL_OP_GATT_PROXY_SET:
                  ESP_LOGI(TAG, "CfgClient:proxy,0x%x", param->status_cb.gatt_proxy_status.gatt_proxy);
                  break;
              case ESP_BLE_MESH_MODEL_OP_RELAY_SET:
                  ESP_LOGI(TAG, "CfgClient:relay,0x%x, retransmit: 0x%x", param->status_cb.relay_status.relay, param->status_cb.relay_status.retransmit);
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET:
                  if (param->status_cb.model_pub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:PublishSet,OK,0x%x", param->status_cb.model_pub_status.publish_addr);
                  } else {
                      ESP_LOGI(TAG, "CfgClient:PublishSet,Fail");
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD:
                  if (param->status_cb.model_sub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CnfClient:SubAdd,OK,%x,%x", param->status_cb.model_sub_status.element_addr, param->status_cb.model_sub_status.sub_addr);
                  } else {
                      ESP_LOGI(TAG, "CnfClient:SubAdd,Fail,%x", param->status_cb.model_sub_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE:
                  if (param->status_cb.model_sub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CnfClient:SubDel,OK,%x,%x", param->status_cb.model_sub_status.element_addr, param->status_cb.model_sub_status.sub_addr);
                  } else {
                      ESP_LOGI(TAG, "CnfClient:SubDel,Fail,%x", param->status_cb.model_sub_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_OVERWRITE:
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_ADD:
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_DELETE:
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_OVERWRITE:
                  break;
              case ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD:
                  if (param->status_cb.netkey_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:NetKeyAdd,OK");
                  } else {
                      ESP_LOGI(TAG, "CfgClient:NetKeyAdd,Fail,%d", param->status_cb.netkey_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD:
                  if (param->status_cb.appkey_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CnfClient:AddAppkey,OK,%x,%x,%x", param->status_cb.appkey_status.net_idx, param->status_cb.appkey_status.app_idx, param->params->ctx.addr);
                  } else {
                      ESP_LOGI(TAG, "CnfClient:AddAppkey,Fail,%x", param->status_cb.appkey_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND:
                  if (param->status_cb.model_app_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CnfClient:AppkeyBind,OK,%x,%x,%x", param->status_cb.model_app_status.app_idx, param->status_cb.model_app_status.model_id, param->params->ctx.addr);
                  } else {
                      ESP_LOGI(TAG, "CnfClient:AppkeyBind,Fail,%x", param->status_cb.model_app_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_FRIEND_SET:
                  ESP_LOGI(TAG, "CfgClient:friend: 0x%x", param->status_cb.friend_status.friend_state);
                  break;
              case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET:
                  if (param->status_cb.heartbeat_pub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatPubSet,OK,destination:0x%x,countlog:0x%x, periodlog:0x%x,ttl:0x%x,features:0x%x,net_idx: 0x%x",
                               param->status_cb.heartbeat_pub_status.dst, param->status_cb.heartbeat_pub_status.count, param->status_cb.heartbeat_pub_status.period,
                               param->status_cb.heartbeat_pub_status.ttl, param->status_cb.heartbeat_pub_status.features, param->status_cb.heartbeat_pub_status.net_idx);
                  } else {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatSet,Fail,%d", param->status_cb.heartbeat_pub_status.status);
                  }
                  break;
              case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET:
                  if (param->status_cb.heartbeat_sub_status.status == ESP_OK) {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatSubSet,OK,source:0x%x,destination:0x%x, periodlog:0x%x,countlog:0x%x,minhops:0x%x,maxhops:0x%x",
                               param->status_cb.heartbeat_sub_status.src, param->status_cb.heartbeat_sub_status.dst, param->status_cb.heartbeat_sub_status.period,
                               param->status_cb.heartbeat_sub_status.count, param->status_cb.heartbeat_sub_status.min_hops, param->status_cb.heartbeat_sub_status.max_hops);
                  } else {
                      ESP_LOGI(TAG, "CfgClient:HeartBeatSubSet,Fail,%d", param->status_cb.heartbeat_sub_status.status);
                  }
                  break;
              default:
                  ESP_LOGI(TAG, "Not supported config client set message opcode");
                  break;
              }
              break;
          case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT:
              ESP_LOGI(TAG, "CnfClient:Publish,OK");
              break;
          case ESP_BLE_MESH_CFG_CLIENT_EVT_MAX:
              ESP_LOGI(TAG, "CnfClient:MaxEvt");
              break;
          case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT:
              ESP_LOGI(TAG, "CfgClient:TimeOut");
              break;
          default:
              ESP_LOGI(TAG, "CfgClient:InvalidEvent");
              break;
          }
      } else {
          ESP_LOGI(TAG, "CnfClient:Fail,%d", param->error_code);
      }
      ESP_LOGD(TAG, "exit %s \n", __func__);
  }
  
  int ble_mesh_configuration_client_model_operation(int argc, char **argv)
  {
      int err = ESP_OK;
      const uint8_t *app_key = NULL;
      esp_ble_mesh_cfg_default_ttl_set_t ttl_set;
      esp_ble_mesh_cfg_gatt_proxy_set_t proxy_set;
      esp_ble_mesh_cfg_app_key_add_t app_key_add;
      esp_ble_mesh_cfg_model_pub_set_t mod_pub_set = {
          .company_id = 0xFFFF,
          .cred_flag = false,
          .publish_period = 0,
          .publish_retransmit = 0,
      };
      esp_ble_mesh_cfg_model_sub_add_t mod_sub_add = {
          .company_id = 0xFFFF,
      };
      esp_ble_mesh_cfg_model_sub_delete_t mod_sub_del = {
          .company_id = 0xFFFF,
      };
      esp_ble_mesh_cfg_relay_set_t relay_set;
      esp_ble_mesh_client_common_param_t client_common = {
          .msg_role = ROLE_PROVISIONER,
          .msg_timeout = 0,
          .ctx.send_ttl = 7,
      };
      esp_ble_mesh_cfg_client_get_state_t get_state = {
          .comp_data_get.page = 0,
          .model_pub_get.company_id = 0xFFFF,
      };
      esp_ble_mesh_cfg_model_app_bind_t mod_app_bind = {
          .company_id = 0xFFFF,
      };
  
      client_common.model = ble_mesh_get_model(ESP_BLE_MESH_MODEL_ID_CONFIG_CLI);
  
      ESP_LOGD(TAG, "enter %s \n", __func__);
  
      int nerrors = arg_parse(argc, argv, (void **) &configuration_client_model_operation);
      if (nerrors != 0) {
          arg_print_errors(stderr, configuration_client_model_operation.end, argv[0]);
          return 1;
      }
  
      if (configuration_client_model_operation.opcode->count != 0) {
          client_common.opcode = configuration_client_model_operation.opcode->ival[0];
      }
  
      if (configuration_client_model_operation.net_idx->count != 0) {
          client_common.ctx.net_idx = configuration_client_model_operation.net_idx->ival[0];
          app_key_add.net_idx = configuration_client_model_operation.net_idx->ival[0];
      }
  
      if (configuration_client_model_operation.unicast_address->count != 0) {
          client_common.ctx.addr = configuration_client_model_operation.unicast_address->ival[0];
          get_state.model_pub_get.element_addr = configuration_client_model_operation.unicast_address->ival[0];
          mod_app_bind.element_addr = configuration_client_model_operation.unicast_address->ival[0];
          mod_sub_add.element_addr = configuration_client_model_operation.unicast_address->ival[0];
          mod_sub_del.element_addr = configuration_client_model_operation.unicast_address->ival[0];
          mod_pub_set.element_addr = configuration_client_model_operation.unicast_address->ival[0];
      }
  
      if (configuration_client_model_operation.appkey_index->count != 0) {
          client_common.ctx.app_idx = configuration_client_model_operation.appkey_index->ival[0];
          mod_app_bind.model_app_idx = configuration_client_model_operation.appkey_index->ival[0];
          app_key_add.app_idx = configuration_client_model_operation.appkey_index->ival[0];
          mod_pub_set.publish_app_idx = configuration_client_model_operation.appkey_index->ival[0];
      }
  
      if (configuration_client_model_operation.value->count != 0) {
          ttl_set.ttl = configuration_client_model_operation.value->ival[0];
          proxy_set.gatt_proxy = configuration_client_model_operation.value->ival[0];
          mod_pub_set.publish_ttl = configuration_client_model_operation.value->ival[0];
      }
  
      if (configuration_client_model_operation.addr->count != 0) {
          mod_sub_del.sub_addr = configuration_client_model_operation.addr->ival[0];
          mod_sub_add.sub_addr = configuration_client_model_operation.addr->ival[0];
          mod_pub_set.publish_addr = configuration_client_model_operation.addr->ival[0];
      }
  
      if (configuration_client_model_operation.mod_id->count != 0) {
          mod_app_bind.model_id = configuration_client_model_operation.mod_id->ival[0];
          mod_sub_add.model_id = configuration_client_model_operation.mod_id->ival[0];
          mod_sub_del.model_id = configuration_client_model_operation.mod_id->ival[0];
          get_state.model_pub_get.model_id = configuration_client_model_operation.mod_id->ival[0];;
          mod_pub_set.model_id = configuration_client_model_operation.mod_id->ival[0];
      }
  
      if (configuration_client_model_operation.relay_statue->count != 0) {
          relay_set.relay = configuration_client_model_operation.relay_statue->ival[0];
          mod_pub_set.publish_period = configuration_client_model_operation.relay_statue->ival[0];
      }
  
      if (configuration_client_model_operation.relay_transmit->count != 0) {
          relay_set.relay_retransmit = configuration_client_model_operation.relay_transmit->ival[0];
          mod_pub_set.publish_retransmit = configuration_client_model_operation.relay_transmit->ival[0];
      }
  
      if (configuration_client_model_operation.cid->count != 0) {
          mod_app_bind.company_id = configuration_client_model_operation.cid->ival[0];
          mod_sub_del.company_id = configuration_client_model_operation.cid->ival[0];
          mod_sub_add.company_id = configuration_client_model_operation.cid->ival[0];
          mod_pub_set.company_id = configuration_client_model_operation.cid->ival[0];
      }
  
      if (configuration_client_model_operation.action_type->count != 0) {
          if (strcmp(configuration_client_model_operation.action_type->sval[0], "get") == 0) {
              err = esp_ble_mesh_config_client_get_state(&client_common, &get_state);
          } else if (strcmp(configuration_client_model_operation.action_type->sval[0], "set") == 0) {
              if (configuration_client_model_operation.set_state->count != 0) {
                  if (strcmp(configuration_client_model_operation.set_state->sval[0], "appkey") == 0) {
                      app_key = esp_ble_mesh_provisioner_get_local_app_key(app_key_add.net_idx, app_key_add.app_idx);
                      if (app_key == NULL) {
                          ESP_LOGE(TAG, "CnfClient:AddAppkey,Fail,app key or network key NULL");
                          return ESP_FAIL;
                      } else {
                          memcpy(app_key_add.app_key, app_key, 16);
                      }
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&app_key_add);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "appbind") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_app_bind);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "ttl") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&ttl_set);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "proxy") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&proxy_set);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "subadd") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_sub_add);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "subdel") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_sub_del);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "relay") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&relay_set);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "pubset") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_pub_set);
                  } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "reset") == 0) {
                      err = esp_ble_mesh_config_client_set_state(&client_common, NULL);
                  }
              }
          } else if (strcmp(configuration_client_model_operation.action_type->sval[0], "reg") == 0) {
              err = esp_ble_mesh_register_config_client_callback(ble_mesh_configuration_client_model_cb);
          }
      }
  
      if (err == ESP_OK) {
          ESP_LOGI(TAG, "ConfigClient:OK");
      } else {
          ESP_LOGI(TAG, "ConfigClient:Fail");
      }
  
      ESP_LOGD(TAG, "exit %s %d\n", __func__, err);
      return err;
  }
  
  void ble_mesh_register_configuration_client_model_command(void)
  {
      configuration_client_model_operation.action_type = arg_str1("z", NULL, "<action>", "action type");
      configuration_client_model_operation.set_state = arg_str0("x", NULL, "<state>", "set state");
      configuration_client_model_operation.opcode = arg_int0("o", NULL, "<opcode>", "message opcode");
      configuration_client_model_operation.unicast_address = arg_int0("u", NULL, "<address>", "unicast address");
      configuration_client_model_operation.net_idx = arg_int0("n", NULL, "<network>", "net work index");
      configuration_client_model_operation.appkey_index = arg_int0("i", NULL, "<index>", "appkey index");
      configuration_client_model_operation.relay_statue = arg_int0("r", NULL, "<relay>", "relay statue");
      configuration_client_model_operation.relay_transmit = arg_int0("t", NULL, "<transmit>", "relay transmit");
      configuration_client_model_operation.cid = arg_int0("c", NULL, "<cid>", "company id");
      configuration_client_model_operation.value = arg_int0("v", NULL, "<value>", "value");
      configuration_client_model_operation.addr = arg_int0("a", NULL, "<address>", "address");
      configuration_client_model_operation.mod_id = arg_int0("m", NULL, "<mod id>", "model id");
      configuration_client_model_operation.end = arg_end(1);
  
      const esp_console_cmd_t client_stconfiguration_client_model_operationate_cmd = {
          .command = "bmccm",
          .help = "ble mesh configuration client model",
          .hint = NULL,
          .func = &ble_mesh_configuration_client_model_operation,
          .argtable = &configuration_client_model_operation,
      };
      ESP_ERROR_CHECK(esp_console_cmd_register(&client_stconfiguration_client_model_operationate_cmd));
  }
  #endif
  