/** @file
 *  @brief Bluetooth Mesh shell
 *
 */

/*
 * Copyright (c) 2017 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "nimble/porting/nimble/include/syscfg/syscfg.h"

#if MYNEWT_VAL(BLE_MESH_SHELL)

#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include "nimble/nimble/host/mesh/src/shell.h"
#include "nimble/console/console.h"
#include "nimble/nimble/host/mesh/include/mesh/mesh.h"
#include "nimble/nimble/host/mesh/include/mesh/main.h"
#include "nimble/nimble/host/mesh/include/mesh/glue.h"
#include "nimble/nimble/host/mesh/include/mesh/testing.h"

/* Private includes for raw Network & Transport layer access */
#include "net.h"
#include "rpl.h"
#include "access.h"
#include "mesh_priv.h"
#include "lpn.h"
#include "transport.h"
#include "foundation.h"
#include "testing.h"
#include "settings.h"

#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
#include "nimble/nimble/host/mesh/include/mesh/model_srv.h"
#include "nimble/nimble/host/mesh/include/mesh/model_cli.h"
#include "light_model.h"
#endif

/* This should be higher priority (lower value) than main task priority */
#define BLE_MESH_SHELL_TASK_PRIO 126
#define BLE_MESH_SHELL_STACK_SIZE 768

OS_TASK_STACK_DEFINE(g_blemesh_shell_stack, BLE_MESH_SHELL_STACK_SIZE);

struct os_task mesh_shell_task;
static struct os_eventq mesh_shell_queue;

#define CID_NVAL   0xffff
#define CID_VENDOR  0x05C3

/* Vendor Model data */
#define VND_MODEL_ID_1 0x1234

/* Default net, app & dev key values, unless otherwise specified */
static const uint8_t default_key[16] = {
	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
	0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
};

static struct {
	uint16_t local;
	uint16_t dst;
	uint16_t net_idx;
	uint16_t app_idx;
} net = {
	.local = BT_MESH_ADDR_UNASSIGNED,
	.dst = BT_MESH_ADDR_UNASSIGNED,
};

#define CUR_FAULTS_MAX 4

static uint8_t cur_faults[CUR_FAULTS_MAX];
static uint8_t reg_faults[CUR_FAULTS_MAX * 2];

static void get_faults(uint8_t *faults, uint8_t faults_size, uint8_t *dst, uint8_t *count)
{
	uint8_t i, limit = *count;

	for (i = 0, *count = 0; i < faults_size && *count < limit; i++) {
		if (faults[i]) {
			*dst++ = faults[i];
			(*count)++;
		}
	}
}

static int fault_get_cur(struct bt_mesh_model *model, uint8_t *test_id,
			 uint16_t *company_id, uint8_t *faults, uint8_t *fault_count)
{
	printk("Sending current faults\n");

	*test_id = 0x00;
	*company_id = CID_VENDOR;

	get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);

	return 0;
}

static int fault_get_reg(struct bt_mesh_model *model, uint16_t cid,
			 uint8_t *test_id, uint8_t *faults, uint8_t *fault_count)
{
	if (cid != CID_VENDOR) {
		printk("Faults requested for unknown Company ID 0x%04x\n", cid);
		return -EINVAL;
	}

	printk("Sending registered faults\n");

	*test_id = 0x00;

	get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);

	return 0;
}

static int fault_clear(struct bt_mesh_model *model, uint16_t cid)
{
	if (cid != CID_VENDOR) {
		return -EINVAL;
	}

	memset(reg_faults, 0, sizeof(reg_faults));

	return 0;
}

static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
		      uint16_t cid)
{
	if (cid != CID_VENDOR) {
		return -EINVAL;
	}

	if (test_id != 0x00) {
		return -EINVAL;
	}

	return 0;
}

static const struct bt_mesh_health_srv_cb health_srv_cb = {
	.fault_get_cur = fault_get_cur,
	.fault_get_reg = fault_get_reg,
	.fault_clear = fault_clear,
	.fault_test = fault_test,
};

static struct bt_mesh_health_srv health_srv = {
	.cb = &health_srv_cb,
};

static struct bt_mesh_model_pub health_pub;

static void
health_pub_init(void)
{
	health_pub.msg  = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX);
}
#if MYNEWT_VAL(BLE_MESH_CFG_CLI)

static struct bt_mesh_cfg_cli cfg_cli = {
};

#endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */

#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
static void show_faults(uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count)
{
	size_t i;

	if (!fault_count) {
		printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n",
		       test_id, cid);
		return;
	}

	printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n",
	       test_id, cid, fault_count);

	for (i = 0; i < fault_count; i++) {
		printk("\t0x%02x\n", faults[i]);
	}
}

static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr,
				  uint8_t test_id, uint16_t cid, uint8_t *faults,
				  size_t fault_count)
{
	printk("Health Current Status from 0x%04x\n", addr);
	show_faults(test_id, cid, faults, fault_count);
}

static struct bt_mesh_health_cli health_cli = {
	.current_status = health_current_status,
};

#endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */

#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
static struct bt_mesh_gen_model_cli gen_onoff_cli;
static struct bt_mesh_model_pub gen_onoff_cli_pub;
static struct bt_mesh_model_pub gen_onoff_srv_pub;
static struct bt_mesh_gen_model_cli gen_level_cli;
static struct bt_mesh_model_pub gen_level_cli_pub;
static struct bt_mesh_model_pub gen_level_srv_pub;
static struct bt_mesh_model_pub light_lightness_pub;
static struct bt_mesh_gen_onoff_srv gen_onoff_srv = {
	.get = light_model_gen_onoff_get,
	.set = light_model_gen_onoff_set,
};
static struct bt_mesh_gen_level_srv gen_level_srv = {
	.get = light_model_gen_level_get,
	.set = light_model_gen_level_set,
};
static struct bt_mesh_light_lightness_srv light_lightness_srv = {
	.get = light_model_light_lightness_get,
	.set = light_model_light_lightness_set,
};

void bt_mesh_set_gen_onoff_srv_cb(int (*get)(struct bt_mesh_model *model, uint8_t *state),
				  int (*set)(struct bt_mesh_model *model, uint8_t state))
{
	gen_onoff_srv.get = get;
	gen_onoff_srv.set = set;
}

void bt_mesh_set_gen_level_srv_cb(int (*get)(struct bt_mesh_model *model, int16_t *level),
				  int (*set)(struct bt_mesh_model *model, int16_t level))
{
	gen_level_srv.get = get;
	gen_level_srv.set = set;
}

void bt_mesh_set_light_lightness_srv_cb(int (*get)(struct bt_mesh_model *model, int16_t *level),
					int (*set)(struct bt_mesh_model *model, int16_t level))
{
	light_lightness_srv.get = get;
	light_lightness_srv.set = set;
}
#endif

static struct bt_mesh_model root_models[] = {
	BT_MESH_MODEL_CFG_SRV,
	BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
#endif
#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
	BT_MESH_MODEL_HEALTH_CLI(&health_cli),
#endif
#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
	BT_MESH_MODEL_GEN_ONOFF_SRV(&gen_onoff_srv, &gen_onoff_srv_pub),
	BT_MESH_MODEL_GEN_ONOFF_CLI(&gen_onoff_cli, &gen_onoff_cli_pub),
	BT_MESH_MODEL_GEN_LEVEL_SRV(&gen_level_srv, &gen_level_srv_pub),
	BT_MESH_MODEL_GEN_LEVEL_CLI(&gen_level_cli, &gen_level_cli_pub),
	BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(&light_lightness_srv, &light_lightness_pub),
#endif
};

static struct bt_mesh_model vnd_models[] = {
	BT_MESH_MODEL_VND(CID_VENDOR, VND_MODEL_ID_1,
			  BT_MESH_MODEL_NO_OPS, NULL, NULL),
};

static struct bt_mesh_elem elements[] = {
	BT_MESH_ELEM(0, root_models, vnd_models),
};

static const struct bt_mesh_comp comp = {
	.cid = CID_VENDOR,
	.elem = elements,
	.elem_count = ARRAY_SIZE(elements),
};

static uint8_t hex2val(char c)
{
	if (c >= '0' && c <= '9') {
		return c - '0';
	} else if (c >= 'a' && c <= 'f') {
		return c - 'a' + 10;
	} else if (c >= 'A' && c <= 'F') {
		return c - 'A' + 10;
	} else {
		return 0;
	}
}

int char2hex(char c, uint8_t *x)
{
	if (c >= '0' && c <= '9') {
		*x = c - '0';
	} else if (c >= 'a' && c <= 'f') {
		*x = c - 'a' + 10;
	} else if (c >= 'A' && c <= 'F') {
		*x = c - 'A' + 10;
	} else {
		return -EINVAL;
	}

	return 0;
}

int hex2char(uint8_t x, char *c)
{
	if (x <= 9) {
		*c = x + '0';
	} else  if (x <= 15) {
		*c = x - 10 + 'a';
	} else {
		return -EINVAL;
	}

	return 0;
}

size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen)
{
	if ((hexlen + 1) < buflen * 2) {
		return 0;
	}

	for (size_t i = 0; i < buflen; i++) {
		if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) {
			return 0;
		}
		if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) {
			return 0;
		}
	}

	hex[2 * buflen] = '\0';
	return 2 * buflen;
}

static size_t hex2bin(const char *hex, uint8_t *bin, size_t bin_len)
{
	size_t len = 0;

	while (*hex && len < bin_len) {
		bin[len] = hex2val(*hex++) << 4;

		if (!*hex) {
			len++;
			break;
		}

		bin[len++] |= hex2val(*hex++);
	}

	return len;
}

static void prov_complete(uint16_t net_idx, uint16_t addr)
{
	printk("Local node provisioned, net_idx 0x%04x address 0x%04x\n",
	       net_idx, addr);
	net.local = addr;
	net.net_idx = net_idx,
	net.dst = addr;
}

static void prov_node_added(uint16_t net_idx, uint8_t uuid[16], uint16_t addr,
			    uint8_t num_elem)
{
	printk("Node provisioned, net_idx 0x%04x address "
	       "0x%04x elements %d", net_idx, addr, num_elem);

	net.net_idx = net_idx,
	net.dst = addr;
}

static void prov_input_complete(void)
{
	printk("Input complete");
}

static void prov_reset(void)
{
	printk("The local node has been reset and needs reprovisioning\n");
}

static int output_number(bt_mesh_output_action_t action, uint32_t number)
{
	printk("OOB Number: %lu\n", number);
	return 0;
}

static int output_string(const char *str)
{
	printk("OOB String: %s\n", str);
	return 0;
}

static bt_mesh_input_action_t input_act;
static uint8_t input_size;

static int cmd_input_num(int argc, char *argv[])
{
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	if (input_act != BT_MESH_ENTER_NUMBER) {
		printk("A number hasn't been requested!\n");
		return 0;
	}

	if (strlen(argv[1]) < input_size) {
		printk("Too short input (%u digits required)\n",
		       input_size);
		return 0;
	}

	err = bt_mesh_input_number(strtoul(argv[1], NULL, 10));
	if (err) {
		printk("Numeric input failed (err %d)\n", err);
		return 0;
	}

	input_act = BT_MESH_NO_INPUT;
	return 0;
}

struct shell_cmd_help cmd_input_num_help = {
	NULL, "<number>", NULL
};

static int cmd_input_str(int argc, char *argv[])
{
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	if (input_act != BT_MESH_ENTER_STRING) {
		printk("A string hasn't been requested!\n");
		return 0;
	}

	if (strlen(argv[1]) < input_size) {
		printk("Too short input (%u characters required)\n",
		       input_size);
		return 0;
	}

	err = bt_mesh_input_string(argv[1]);
	if (err) {
		printk("String input failed (err %d)\n", err);
		return 0;
	}

	input_act = BT_MESH_NO_INPUT;
	return 0;
}

struct shell_cmd_help cmd_input_str_help = {
	NULL, "<string>", NULL
};

static int input(bt_mesh_input_action_t act, uint8_t size)
{
	switch (act) {
	case BT_MESH_ENTER_NUMBER:
		printk("Enter a number (max %u digits) with: input-num <num>\n",
		       size);
		break;
	case BT_MESH_ENTER_STRING:
		printk("Enter a string (max %u chars) with: input-str <str>\n",
		       size);
		break;
	default:
		printk("Unknown input action %u (size %u) requested!\n",
		       act, size);
		return -EINVAL;
	}

	input_act = act;
	input_size = size;
	return 0;
}

static const char *bearer2str(bt_mesh_prov_bearer_t bearer)
{
	switch (bearer) {
	case BT_MESH_PROV_ADV:
		return "PB-ADV";
	case BT_MESH_PROV_GATT:
		return "PB-GATT";
	default:
		return "unknown";
	}
}

static void link_open(bt_mesh_prov_bearer_t bearer)
{
	printk("Provisioning link opened on %s\n", bearer2str(bearer));
}

static void link_close(bt_mesh_prov_bearer_t bearer)
{
	printk("Provisioning link closed on %s\n", bearer2str(bearer));
}

static uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);

static uint8_t static_val[16];

static struct bt_mesh_prov prov = {
	.uuid = dev_uuid,
	.link_open = link_open,
	.link_close = link_close,
	.complete = prov_complete,
	.node_added = prov_node_added,
	.reset = prov_reset,
	.static_val = NULL,
	.static_val_len = 0,
	.output_size = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_SIZE),
	.output_actions = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_ACTIONS),
	.output_number = output_number,
	.output_string = output_string,
	.input_size = MYNEWT_VAL(BLE_MESH_OOB_INPUT_SIZE),
	.input_actions = MYNEWT_VAL(BLE_MESH_OOB_INPUT_ACTIONS),
	.input = input,
	.input_complete = prov_input_complete,
};

static int cmd_static_oob(int argc, char *argv[])
{
	if (argc < 2) {
		prov.static_val = NULL;
		prov.static_val_len = 0;
	} else {
		prov.static_val_len = hex2bin(argv[1], static_val, 16);
		if (prov.static_val_len) {
			prov.static_val = static_val;
		} else {
			prov.static_val = NULL;
		}
	}

	if (prov.static_val) {
		printk("Static OOB value set (length %u)\n",
		       prov.static_val_len);
	} else {
		printk("Static OOB value cleared\n");
	}

	return 0;
}

struct shell_cmd_help cmd_static_oob_help = {
	NULL, "[val: 1-16 hex values]", NULL
};

static int cmd_uuid(int argc, char *argv[])
{
	uint8_t uuid[16];
	size_t len;

	if (argc < 2) {
		return -EINVAL;
	}

	len = hex2bin(argv[1], uuid, sizeof(uuid));
	if (len < 1) {
		return -EINVAL;
	}

	memcpy(dev_uuid, uuid, len);
	memset(dev_uuid + len, 0, sizeof(dev_uuid) - len);

	printk("Device UUID set\n");

	return 0;
}

struct shell_cmd_help cmd_uuid_help = {
	NULL, "<UUID: 1-16 hex values>", NULL
};

static int cmd_reset(int argc, char *argv[])
{
	uint16_t addr;
	if (argc < 2) {
		return -EINVAL;
	}

	addr = strtoul(argv[1], NULL, 0);

	if (addr == net.local) {
		bt_mesh_reset();
		printk("Local node reset complete");
	} else {
		int err;
		bool reset = false;

		err = bt_mesh_cfg_node_reset(net.net_idx, net.dst, &reset);
		if (err) {
			printk("Unable to send "
					"Remote Node Reset (err %d)", err);
			return 0;
		}

		printk("Remote node reset complete");
	}

	return 0;
}

struct shell_cmd_help cmd_reset_help = {
	NULL, "<addr>", NULL
};

static uint8_t str2u8(const char *str)
{
	if (isdigit(str[0])) {
		return strtoul(str, NULL, 0);
	}

	return (!strcmp(str, "on") || !strcmp(str, "enable"));
}

static bool str2bool(const char *str)
{
	return str2u8(str);
}

#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
static int cmd_lpn(int argc, char *argv[])
{
	static bool enabled;
	int err;

	if (argc < 2) {
		printk("%s\n", enabled ? "enabled" : "disabled");
		return 0;
	}

	if (str2bool(argv[1])) {
		if (enabled) {
			printk("LPN already enabled\n");
			return 0;
		}

		err = bt_mesh_lpn_set(true);
		if (err) {
			printk("Enabling LPN failed (err %d)\n", err);
		} else {
			enabled = true;
		}
	} else {
		if (!enabled) {
			printk("LPN already disabled\n");
			return 0;
		}

		err = bt_mesh_lpn_set(false);
		if (err) {
			printk("Enabling LPN failed (err %d)\n", err);
		} else {
			enabled = false;
		}
	}

	return 0;
}

static int cmd_poll(int argc, char *argv[])
{
	int err;

	err = bt_mesh_lpn_poll();
	if (err) {
		printk("Friend Poll failed (err %d)\n", err);
	}

	return 0;
}

static void lpn_cb(uint16_t friend_addr, bool established)
{
	if (established) {
		printk("Friendship (as LPN) established to Friend 0x%04x\n",
		       friend_addr);
	} else {
		printk("Friendship (as LPN) lost with Friend 0x%04x\n",
		       friend_addr);
	}
}

struct shell_cmd_help cmd_lpn_help = {
	NULL, "<value: off, on>", NULL
};

#endif /* MESH_LOW_POWER */

static int check_pub_addr_unassigned(void)
{
#ifdef ARCH_sim
	return 0;
#else
	uint8_t addr[BLE_DEV_ADDR_LEN];

	return ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL) != 0;
#endif
}

int cmd_mesh_init(int argc, char *argv[])
{
	int err;
	ble_addr_t addr;

	if (check_pub_addr_unassigned()) {
		/* Use NRPA */
		err = ble_hs_id_gen_rnd(1, &addr);
		assert(err == 0);
		err = ble_hs_id_set_rnd(addr.val);
		assert(err == 0);

		err = bt_mesh_init(addr.type, &prov, &comp);
	}
	else {
		err = bt_mesh_init(0, &prov, &comp);
	}

	if (err) {
		printk("Mesh initialization failed (err %d)\n", err);
	}

	printk("Mesh initialized\n");

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		settings_load();
	}

	if (bt_mesh_is_provisioned()) {
		printk("Mesh network restored from flash\n");
	} else {
		printk("Use \"pb-adv on\" or \"pb-gatt on\" to enable"
		       " advertising\n");
	}

#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
	bt_mesh_lpn_set_cb(lpn_cb);
#endif

	return 0;
}

#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
static int cmd_ident(int argc, char *argv[])
{
	int err;

	err = bt_mesh_proxy_identity_enable();
	if (err) {
		printk("Failed advertise using Node Identity (err %d)\n", err);
	}

	return 0;
}
#endif /* MESH_GATT_PROXY */

static int cmd_dst(int argc, char *argv[])
{
	if (argc < 2) {
		printk("Destination address: 0x%04x%s\n", net.dst,
		       net.dst == net.local ? " (local)" : "");
		return 0;
	}

	if (!strcmp(argv[1], "local")) {
		net.dst = net.local;
	} else {
		net.dst = strtoul(argv[1], NULL, 0);
	}

	printk("Destination address set to 0x%04x%s\n", net.dst,
	       net.dst == net.local ? " (local)" : "");
	return 0;
}

struct shell_cmd_help cmd_dst_help = {
	NULL, "[destination address]", NULL
};

static int cmd_netidx(int argc, char *argv[])
{
	if (argc < 2) {
		printk("NetIdx: 0x%04x\n", net.net_idx);
		return 0;
	}

	net.net_idx = strtoul(argv[1], NULL, 0);
	printk("NetIdx set to 0x%04x\n", net.net_idx);
	return 0;
}

struct shell_cmd_help cmd_netidx_help = {
	NULL, "[NetIdx]", NULL
};

static int cmd_appidx(int argc, char *argv[])
{
	if (argc < 2) {
		printk("AppIdx: 0x%04x\n", net.app_idx);
		return 0;
	}

	net.app_idx = strtoul(argv[1], NULL, 0);
	printk("AppIdx set to 0x%04x\n", net.app_idx);
	return 0;
}

struct shell_cmd_help cmd_appidx_help = {
	NULL, "[AppIdx]", NULL
};

static int cmd_net_send(int argc, char *argv[])
{
	struct os_mbuf *msg = NET_BUF_SIMPLE(32);
	struct bt_mesh_msg_ctx ctx = {
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.net_idx = net.net_idx,
		.addr = net.dst,
		.app_idx = net.app_idx,

	};
	struct bt_mesh_net_tx tx = {
		.ctx = &ctx,
		.src = net.local,
	};
	size_t len;
	int err = 0;

	if (argc < 2) {
		err = -EINVAL;
		goto done;
	}

	net_buf_simple_init(msg, 0);
	len = hex2bin(argv[1], msg->om_data, net_buf_simple_tailroom(msg) - 4);
	net_buf_simple_add(msg, len);

	err = bt_mesh_trans_send(&tx, msg, NULL, NULL);
	if (err) {
		printk("Failed to send (err %d)\n", err);
	}

done:
	os_mbuf_free_chain(msg);
	return err;
}

struct shell_cmd_help cmd_net_send_help = {
	NULL, "<hex string>", NULL
};

static int cmd_rpl_clear(int argc, char *argv[])
{
	bt_mesh_rpl_clear();
	return 0;
}

#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
static int cmd_lpn_subscribe(int argc, char *argv[])
{
	uint16_t address;

	if (argc < 2) {
		return -EINVAL;
	}

	address = strtoul(argv[1], NULL, 0);

	printk("address 0x%04x", address);

	bt_mesh_lpn_group_add(address);

	return 0;
}

struct shell_cmd_help cmd_lpn_subscribe_help = {
	NULL, "<addr>", NULL
};

static int cmd_lpn_unsubscribe(int argc, char *argv[])
{
	uint16_t address;

	if (argc < 2) {
		return -EINVAL;
	}

	address = strtoul(argv[1], NULL, 0);

	printk("address 0x%04x", address);

	bt_mesh_lpn_group_del(&address, 1);

	return 0;
}

struct shell_cmd_help cmd_lpn_unsubscribe_help = {
	NULL, "<addr>", NULL
};
#endif

#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST)
static int cmd_iv_update(int argc, char *argv[])
{
	if (bt_mesh_iv_update()) {
		printk("Transitioned to IV Update In Progress state\n");
	} else {
		printk("Transitioned to IV Update Normal state\n");
	}

	printk("IV Index is 0x%08lx\n", bt_mesh.iv_index);

	return 0;
}

static int cmd_iv_update_test(int argc, char *argv[])
{
	bool enable;

	if (argc < 2) {
		return -EINVAL;
	}

	enable = str2bool(argv[1]);
	if (enable) {
		printk("Enabling IV Update test mode\n");
	} else {
		printk("Disabling IV Update test mode\n");
	}

	bt_mesh_iv_update_test(enable);

	return 0;
}

struct shell_cmd_help cmd_iv_update_test_help = {
	NULL, "<value: off, on>", NULL
};
#endif

#if MYNEWT_VAL(BLE_MESH_CFG_CLI)

int cmd_timeout(int argc, char *argv[])
{
	int32_t timeout;

	if (argc < 2) {
		timeout = bt_mesh_cfg_cli_timeout_get();
		if (timeout == K_FOREVER) {
			printk("Message timeout: forever\n");
		} else {
			printk("Message timeout: %lu seconds\n",
			       timeout / 1000);
		}

		return 0;
	}

	timeout = strtol(argv[1], NULL, 0);
	if (timeout < 0 || timeout > (INT32_MAX / 1000)) {
		timeout = K_FOREVER;
	} else {
		timeout = timeout * 1000;
	}

	bt_mesh_cfg_cli_timeout_set(timeout);
	if (timeout == K_FOREVER) {
		printk("Message timeout: forever\n");
	} else {
		printk("Message timeout: %lu seconds\n",
		       timeout / 1000);
	}

	return 0;
}

struct shell_cmd_help cmd_timeout_help = {
	NULL, "[timeout in seconds]", NULL
};


static int cmd_get_comp(int argc, char *argv[])
{
	struct os_mbuf *buf = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX);
	struct bt_mesh_comp_p0_elem elem;
	struct bt_mesh_comp_p0 comp;
	uint8_t page = 0x00;
	int err = 0;

	if (argc > 1) {
		page = strtol(argv[1], NULL, 0);
	}

	net_buf_simple_init(buf, 0);
	err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page, &page,
					buf);
	if (err) {
		printk("Getting composition failed (err %d)\n", err);
		goto done;
	}

	if (page != 0x00) {
		printk("Got page 0x%02x. No parser available.",
			    page);
		goto done;
	}

	err = bt_mesh_comp_p0_get(&comp, buf);
	if (err) {
		printk("Getting composition failed (err %d)\n", err);
		goto done;
	}

	printk("Got Composition Data for 0x%04x:", net.dst);
	printk("\tCID      0x%04x", comp.cid);
	printk("\tPID      0x%04x", comp.pid);
	printk("\tVID      0x%04x", comp.vid);
	printk("\tCRPL     0x%04x", comp.crpl);
	printk("\tFeatures 0x%04x", comp.feat);

	while (bt_mesh_comp_p0_elem_pull(&comp, &elem)) {
		int i;

		printk("\tElement @ 0x%04x:", elem.loc);
		if (elem.nsig) {
			printk("\t\tSIG Models:\n");
		} else {
			printk("\t\tNo SIG Models\n");
		}

		for (i = 0; i < elem.nsig; i++) {
			uint16_t mod_id = bt_mesh_comp_p0_elem_mod(&elem, i);

			printk("\t\t\t0x%04x\n", mod_id);
		}

		if (elem.nvnd) {
			printk("\t\tVendor Models:\n");
		} else {
			printk("\t\tNo Vendor Models\n");
		}

		for (i = 0; i < elem.nvnd; i++) {
			struct bt_mesh_mod_id_vnd mod =
				bt_mesh_comp_p0_elem_mod_vnd(&elem, i);

			printk("\t\t\tCompany 0x%04x: 0x%04x",
				    mod.company, mod.id);
		}
	}
	if (buf->om_len) {
		printk("\t\t...truncated data!");
	}

done:
	os_mbuf_free_chain(buf);
	return err;
}

struct shell_cmd_help cmd_get_comp_help = {
	NULL, "[page]", NULL
};

static int cmd_beacon(int argc, char *argv[])
{
	uint8_t status;
	int err;

	if (argc < 2) {
		err = bt_mesh_cfg_beacon_get(net.net_idx, net.dst, &status);
	} else {
		uint8_t val = str2u8(argv[1]);

		err = bt_mesh_cfg_beacon_set(net.net_idx, net.dst, val,
					     &status);
	}

	if (err) {
		printk("Unable to send Beacon Get/Set message (err %d)\n", err);
		return 0;
	}

	printk("Beacon state is 0x%02x\n", status);

	return 0;
}

static void print_unprovisioned_beacon(uint8_t uuid[16],
				       bt_mesh_prov_oob_info_t oob_info,
				       uint32_t *uri_hash)
{
	char uuid_hex_str[32 + 1];

	bin2hex(uuid, 16, uuid_hex_str, sizeof(uuid_hex_str));

	printk("UUID %s, OOB Info 0x%04x, URI Hash 0x%lx",
		   uuid_hex_str, oob_info,
		   (uri_hash == NULL ? 0 : *uri_hash));
}

static int cmd_beacon_listen(int argc, char *argv[])
{
	uint8_t val = str2u8(argv[1]);

	if (val) {
		prov.unprovisioned_beacon = print_unprovisioned_beacon;
	} else {
		prov.unprovisioned_beacon = NULL;
	}

	return 0;
}

struct shell_cmd_help cmd_beacon_help = {
	NULL, "[val: off, on]", NULL
};

struct shell_cmd_help cmd_beacon_listen_help = {
	NULL, "[val: off, on]", NULL
};

static int cmd_ttl(int argc, char *argv[])
{
	uint8_t ttl;
	int err;

	if (argc < 2) {
		err = bt_mesh_cfg_ttl_get(net.net_idx, net.dst, &ttl);
	} else {
		uint8_t val = strtoul(argv[1], NULL, 0);

		err = bt_mesh_cfg_ttl_set(net.net_idx, net.dst, val, &ttl);
	}

	if (err) {
		printk("Unable to send Default TTL Get/Set (err %d)\n", err);
		return 0;
	}

	printk("Default TTL is 0x%02x\n", ttl);

	return 0;
}

struct shell_cmd_help cmd_ttl_help = {
	NULL, "[ttl: 0x00, 0x02-0x7f]", NULL
};

static int cmd_friend(int argc, char *argv[])
{
	uint8_t frnd;
	int err;

	if (argc < 2) {
		err = bt_mesh_cfg_friend_get(net.net_idx, net.dst, &frnd);
	} else {
		uint8_t val = str2u8(argv[1]);

		err = bt_mesh_cfg_friend_set(net.net_idx, net.dst, val, &frnd);
	}

	if (err) {
		printk("Unable to send Friend Get/Set (err %d)\n", err);
		return 0;
	}

	printk("Friend is set to 0x%02x\n", frnd);

	return 0;
}

struct shell_cmd_help cmd_friend_help = {
	NULL, "[val: off, on]", NULL
};

static int cmd_gatt_proxy(int argc, char *argv[])
{
	uint8_t proxy;
	int err;

	if (argc < 2) {
		err = bt_mesh_cfg_gatt_proxy_get(net.net_idx, net.dst, &proxy);
	} else {
		uint8_t val = str2u8(argv[1]);

		err = bt_mesh_cfg_gatt_proxy_set(net.net_idx, net.dst, val,
						 &proxy);
	}

	if (err) {
		printk("Unable to send GATT Proxy Get/Set (err %d)\n", err);
		return 0;
	}

	printk("GATT Proxy is set to 0x%02x\n", proxy);

	return 0;
}

struct shell_cmd_help cmd_gatt_proxy_help = {
	NULL, "[val: off, on]", NULL
};

static int cmd_net_transmit(int argc, char *argv[])
{
	uint8_t transmit;
	int err;

	if (argc < 2) {
		err = bt_mesh_cfg_net_transmit_get(net.net_idx,
				net.dst, &transmit);
	} else {
		if (argc != 3) {
			printk("Wrong number of input arguments"
						"(2 arguments are required)");
			return -EINVAL;
		}

		uint8_t count, interval, new_transmit;

		count = strtoul(argv[1], NULL, 0);
		interval = strtoul(argv[2], NULL, 0);

		new_transmit = BT_MESH_TRANSMIT(count, interval);

		err = bt_mesh_cfg_net_transmit_set(net.net_idx, net.dst,
				new_transmit, &transmit);
	}

	if (err) {
		printk("Unable to send network transmit"
				" Get/Set (err %d)", err);
		return 0;
	}

	printk("Transmit 0x%02x (count %u interval %ums)",
			transmit, BT_MESH_TRANSMIT_COUNT(transmit),
			BT_MESH_TRANSMIT_INT(transmit));

	return 0;
}

struct shell_cmd_help cmd_net_transmit_help = {
	NULL, "[<count: 0-7> <interval: 10-320>]", NULL
};

static int cmd_relay(int argc, char *argv[])
{
	uint8_t relay, transmit;
	int err;

	if (argc < 2) {
		err = bt_mesh_cfg_relay_get(net.net_idx, net.dst, &relay,
					    &transmit);
	} else {
		uint8_t val = str2u8(argv[1]);
		uint8_t count, interval, new_transmit;

		if (val) {
			if (argc > 2) {
				count = strtoul(argv[2], NULL, 0);
			} else {
				count = 2;
			}

			if (argc > 3) {
				interval = strtoul(argv[3], NULL, 0);
			} else {
				interval = 20;
			}

			new_transmit = BT_MESH_TRANSMIT(count, interval);
		} else {
			new_transmit = 0;
		}

		err = bt_mesh_cfg_relay_set(net.net_idx, net.dst, val,
					    new_transmit, &relay, &transmit);
	}

	if (err) {
		printk("Unable to send Relay Get/Set (err %d)\n", err);
		return 0;
	}

	printk("Relay is 0x%02x, Transmit 0x%02x (count %u interval %ums)\n",
	       relay, transmit, BT_MESH_TRANSMIT_COUNT(transmit),
	       BT_MESH_TRANSMIT_INT(transmit));

	return 0;
}

struct shell_cmd_help cmd_relay_help = {
	NULL, "[val: off, on] [count: 0-7] [interval: 0-32]", NULL
};

static int cmd_net_key_add(int argc, char *argv[])
{
	bool has_key_val = (argc > 2);
	uint8_t key_val[16];
	uint16_t key_net_idx;
	uint8_t status;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	key_net_idx = strtoul(argv[1], NULL, 0);

	if (has_key_val) {
		size_t len;

		len = hex2bin(argv[3], key_val, sizeof(key_val));
		memset(key_val, 0, sizeof(key_val) - len);
	} else {
		memcpy(key_val, default_key, sizeof(key_val));
	}

	if (IS_ENABLED(CONFIG_BT_MESH_CDB)) {
		struct bt_mesh_cdb_subnet *subnet;

		subnet = bt_mesh_cdb_subnet_get(key_net_idx);
		if (subnet) {
			if (has_key_val) {
				printk("Subnet 0x%03x already has a value", key_net_idx);
				return 0;
			}

			memcpy(key_val, subnet->keys[0].net_key, 16);
		} else {
			subnet = bt_mesh_cdb_subnet_alloc(key_net_idx);
			if (!subnet) {
				printk("No space for subnet in cdb");
				return 0;
			}

			memcpy(subnet->keys[0].net_key, key_val, 16);
			bt_mesh_cdb_subnet_store(subnet);
		}
	}

	err = bt_mesh_cfg_net_key_add(net.net_idx, net.dst, key_net_idx,
				      key_val, &status);
	if (err) {
		printk("Unable to send NetKey Add (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("NetKeyAdd failed with status 0x%02x\n", status);
	} else {
		printk("NetKey added with NetKey Index 0x%03x\n", key_net_idx);
	}

	return 0;
}

struct shell_cmd_help cmd_net_key_add_help = {
	NULL, "<NetKeyIndex> [val]", NULL
};

static int cmd_net_key_get(int argc, char *argv[])
{
	uint16_t keys[16];
	size_t cnt;
	int err, i;

	cnt = ARRAY_SIZE(keys);

	err = bt_mesh_cfg_net_key_get(net.net_idx, net.dst, keys, &cnt);
	if (err) {
		printk("Unable to send NetKeyGet (err %d)", err);
		return 0;
	}

	printk("NetKeys known by 0x%04x:", net.dst);
	for (i = 0; i < cnt; i++) {
		printk("\t0x%03x", keys[i]);
	}

	return 0;
}

struct shell_cmd_help cmd_net_key_get_help = {
	NULL, NULL, NULL
};

static int cmd_net_key_del(int argc, char *argv[])
{
	uint16_t key_net_idx;
	uint8_t status;
	int err;

	key_net_idx = strtoul(argv[1], NULL, 0);

	err = bt_mesh_cfg_net_key_del(net.net_idx, net.dst, key_net_idx,
				      &status);
	if (err) {
		printk("Unable to send NetKeyDel (err %d)", err);
		return 0;
	}

	if (status) {
		printk("NetKeyDel failed with status 0x%02x",
			    status);
	} else {
		printk("NetKey 0x%03x deleted", key_net_idx);
	}

	return 0;
}

struct shell_cmd_help cmd_net_key_del_help = {
	NULL, "<NetKeyIndex>", NULL
};

static int cmd_app_key_add(int argc, char *argv[])
{
	uint8_t key_val[16];
	uint16_t key_net_idx, key_app_idx;
	bool has_key_val = (argc > 3);
	uint8_t status;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	key_net_idx = strtoul(argv[1], NULL, 0);
	key_app_idx = strtoul(argv[2], NULL, 0);

	if (has_key_val) {
		size_t len;

		len = hex2bin(argv[3], key_val, sizeof(key_val));
		memset(key_val, 0, sizeof(key_val) - len);
	} else {
		memcpy(key_val, default_key, sizeof(key_val));
	}

	if (IS_ENABLED(CONFIG_BT_MESH_CDB)) {
		struct bt_mesh_cdb_app_key *app_key;

		app_key = bt_mesh_cdb_app_key_get(key_app_idx);
		if (app_key) {
			if (has_key_val) {
				printk("App key 0x%03x already has a value", key_app_idx);
				return 0;
			}

			memcpy(key_val, app_key->keys[0].app_key, 16);
		} else {
			app_key = bt_mesh_cdb_app_key_alloc(key_net_idx,
							    key_app_idx);
			if (!app_key) {
				printk("No space for app key in cdb");
				return 0;
			}

			memcpy(app_key->keys[0].app_key, key_val, 16);
			bt_mesh_cdb_app_key_store(app_key);
		}
	}

	err = bt_mesh_cfg_app_key_add(net.net_idx, net.dst, key_net_idx,
				      key_app_idx, key_val, &status);
	if (err) {
		printk("Unable to send App Key Add (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("AppKeyAdd failed with status 0x%02x\n", status);
	} else {
		printk("AppKey added, NetKeyIndex 0x%04x AppKeyIndex 0x%04x\n",
		       key_net_idx, key_app_idx);
	}

	return 0;
}

struct shell_cmd_help cmd_app_key_add_help = {
	NULL, "<NetKeyIndex> <AppKeyIndex> [val]", NULL
};

static int cmd_app_key_get(int argc, char *argv[])
{
	uint16_t net_idx;
	uint16_t keys[16];
	size_t cnt;
	uint8_t status;
	int err, i;

	net_idx = strtoul(argv[1], NULL, 0);
	cnt = ARRAY_SIZE(keys);

	err = bt_mesh_cfg_app_key_get(net.net_idx, net.dst, net_idx, &status,
				      keys, &cnt);
	if (err) {
		printk("Unable to send AppKeyGet (err %d)", err);
		return 0;
	}

	if (status) {
		printk("AppKeyGet failed with status 0x%02x",
			    status);
		return 0;
	}

	printk(
		    "AppKeys for NetKey 0x%03x known by 0x%04x:", net_idx,
		    net.dst);
	for (i = 0; i < cnt; i++) {
		printk("\t0x%03x", keys[i]);
	}

	return 0;
}

struct shell_cmd_help cmd_app_key_get_help = {
	NULL, "<NetKeyIndex>", NULL
};

static int cmd_app_key_del(int argc, char *argv[])
{
	uint16_t key_net_idx, key_app_idx;
	uint8_t status;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	key_net_idx = strtoul(argv[1], NULL, 0);
	key_app_idx = strtoul(argv[2], NULL, 0);

	err = bt_mesh_cfg_app_key_del(net.net_idx, net.dst, key_net_idx,
				      key_app_idx, &status);
	if (err) {
		printk("Unable to send App Key del(err %d)", err);
		return 0;
	}

	if (status) {
		printk("AppKeyDel failed with status 0x%02x",
			    status);
	} else {
		printk("AppKey deleted, NetKeyIndex 0x%04x "
			    "AppKeyIndex 0x%04x", key_net_idx, key_app_idx);
	}

	return 0;
}

struct shell_cmd_help cmd_app_key_del_help = {
	NULL, "<NetKeyIndex> <AppKeyIndex>", NULL
};

static int cmd_mod_app_bind(int argc, char *argv[])
{
	uint16_t elem_addr, mod_app_idx, mod_id, cid;
	uint8_t status;
	int err;

	if (argc < 4) {
		return -EINVAL;
	}

	elem_addr = strtoul(argv[1], NULL, 0);
	mod_app_idx = strtoul(argv[2], NULL, 0);
	mod_id = strtoul(argv[3], NULL, 0);

	if (argc > 4) {
		cid = strtoul(argv[4], NULL, 0);
		err = bt_mesh_cfg_mod_app_bind_vnd(net.net_idx, net.dst,
						   elem_addr, mod_app_idx,
						   mod_id, cid, &status);
	} else {
		err = bt_mesh_cfg_mod_app_bind(net.net_idx, net.dst, elem_addr,
					       mod_app_idx, mod_id, &status);
	}

	if (err) {
		printk("Unable to send Model App Bind (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Model App Bind failed with status 0x%02x\n", status);
	} else {
		printk("AppKey successfully bound\n");
	}

	return 0;
}

struct shell_cmd_help cmd_mod_app_bind_help = {
	NULL, "<addr> <AppIndex> <Model ID> [Company ID]", NULL
};

static int cmd_mod_app_unbind(int argc, char *argv[])
{
	uint16_t elem_addr, mod_app_idx, mod_id, cid;
	uint8_t status;
	int err;

	if (argc < 4) {
		return -EINVAL;
	}

	elem_addr = strtoul(argv[1], NULL, 0);
	mod_app_idx = strtoul(argv[2], NULL, 0);
	mod_id = strtoul(argv[3], NULL, 0);

	if (argc > 4) {
		cid = strtoul(argv[4], NULL, 0);
		err = bt_mesh_cfg_mod_app_unbind_vnd(net.net_idx, net.dst,
						   elem_addr, mod_app_idx,
						   mod_id, cid, &status);
	} else {
		err = bt_mesh_cfg_mod_app_unbind(net.net_idx, net.dst,
				elem_addr, mod_app_idx, mod_id, &status);
	}

	if (err) {
		printk("Unable to send Model App Unbind (err %d)",
			    err);
		return 0;
	}

	if (status) {
		printk("Model App Unbind failed with status 0x%02x",
			    status);
	} else {
		printk("AppKey successfully unbound");
	}

	return 0;
}

struct shell_cmd_help cmd_mod_app_unbind_help = {
	NULL, "<addr> <AppIndex> <Model ID> [Company ID]", NULL
};

static int cmd_mod_app_get(int argc,
			      char *argv[])
{
	uint16_t elem_addr, mod_id, cid;
	uint16_t apps[16];
	uint8_t status;
	size_t cnt;
	int err, i;

	elem_addr = strtoul(argv[1], NULL, 0);
	mod_id = strtoul(argv[2], NULL, 0);
	cnt = ARRAY_SIZE(apps);

	if (argc > 3) {
		cid = strtoul(argv[3], NULL, 0);
		err = bt_mesh_cfg_mod_app_get_vnd(net.net_idx, net.dst,
						  elem_addr, mod_id, cid,
						  &status, apps, &cnt);
	} else {
		err = bt_mesh_cfg_mod_app_get(net.net_idx, net.dst, elem_addr,
					      mod_id, &status, apps, &cnt);
	}

	if (err) {
		printk("Unable to send Model App Get (err %d)",
			    err);
		return 0;
	}

	if (status) {
		printk("Model App Get failed with status 0x%02x",
			    status);
	} else {
		printk(
			"Apps bound to Element 0x%04x, Model 0x%04x %s:",
			elem_addr, mod_id, argc > 3 ? argv[3] : "(SIG)");

		if (!cnt) {
			printk("\tNone.");
		}

		for (i = 0; i < cnt; i++) {
			printk("\t0x%04x", apps[i]);
		}
	}

	return 0;
}

struct shell_cmd_help cmd_mod_app_get_help = {
	NULL, "<elem addr> <Model ID> [Company ID]", NULL
};

static int cmd_mod_sub_add(int argc, char *argv[])
{
	uint16_t elem_addr, sub_addr, mod_id, cid;
	uint8_t status;
	int err;

	if (argc < 4) {
		return -EINVAL;
	}

	elem_addr = strtoul(argv[1], NULL, 0);
	sub_addr = strtoul(argv[2], NULL, 0);
	mod_id = strtoul(argv[3], NULL, 0);

	if (argc > 4) {
		cid = strtoul(argv[4], NULL, 0);
		err = bt_mesh_cfg_mod_sub_add_vnd(net.net_idx, net.dst,
						  elem_addr, sub_addr, mod_id,
						  cid, &status);
	} else {
		err = bt_mesh_cfg_mod_sub_add(net.net_idx, net.dst, elem_addr,
					      sub_addr, mod_id, &status);
	}

	if (err) {
		printk("Unable to send Model Subscription Add (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Model Subscription Add failed with status 0x%02x\n",
		       status);
	} else {
		printk("Model subscription was successful\n");
	}

	return 0;
}

struct shell_cmd_help cmd_mod_sub_add_help = {
	NULL, "<elem addr> <sub addr> <Model ID> [Company ID]", NULL
};

static int cmd_mod_sub_del(int argc, char *argv[])
{
	uint16_t elem_addr, sub_addr, mod_id, cid;
	uint8_t status;
	int err;

	if (argc < 4) {
		return -EINVAL;
	}

	elem_addr = strtoul(argv[1], NULL, 0);
	sub_addr = strtoul(argv[2], NULL, 0);
	mod_id = strtoul(argv[3], NULL, 0);

	if (argc > 4) {
		cid = strtoul(argv[4], NULL, 0);
		err = bt_mesh_cfg_mod_sub_del_vnd(net.net_idx, net.dst,
						  elem_addr, sub_addr, mod_id,
						  cid, &status);
	} else {
		err = bt_mesh_cfg_mod_sub_del(net.net_idx, net.dst, elem_addr,
					      sub_addr, mod_id, &status);
	}

	if (err) {
		printk("Unable to send Model Subscription Delete (err %d)\n",
		       err);
		return 0;
	}

	if (status) {
		printk("Model Subscription Delete failed with status 0x%02x\n",
		       status);
	} else {
		printk("Model subscription deltion was successful\n");
	}

	return 0;
}

struct shell_cmd_help cmd_mod_sub_del_help = {
	NULL, "<elem addr> <sub addr> <Model ID> [Company ID]", NULL
};

static int cmd_mod_sub_add_va(int argc, char *argv[])
{
	uint16_t elem_addr, sub_addr, mod_id, cid;
	uint8_t label[16];
	uint8_t status;
	size_t len;
	int err;

	if (argc < 4) {
		return -EINVAL;
	}

	elem_addr = strtoul(argv[1], NULL, 0);

	len = hex2bin(argv[2], label, sizeof(label));
	memset(label + len, 0, sizeof(label) - len);

	mod_id = strtoul(argv[3], NULL, 0);

	if (argc > 4) {
		cid = strtoul(argv[4], NULL, 0);
		err = bt_mesh_cfg_mod_sub_va_add_vnd(net.net_idx, net.dst,
						     elem_addr, label, mod_id,
						     cid, &sub_addr, &status);
	} else {
		err = bt_mesh_cfg_mod_sub_va_add(net.net_idx, net.dst,
						 elem_addr, label, mod_id,
						 &sub_addr, &status);
	}

	if (err) {
		printk("Unable to send Mod Sub VA Add (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Mod Sub VA Add failed with status 0x%02x\n",
		       status);
	} else {
		printk("0x%04x subscribed to Label UUID %s (va 0x%04x)\n",
		       elem_addr, argv[2], sub_addr);
	}

	return 0;
}

struct shell_cmd_help cmd_mod_sub_add_va_help = {
	NULL, "<elem addr> <Label UUID> <Model ID> [Company ID]", NULL
};

static int cmd_mod_sub_del_va(int argc, char *argv[])
{
	uint16_t elem_addr, sub_addr, mod_id, cid;
	uint8_t label[16];
	uint8_t status;
	size_t len;
	int err;

	if (argc < 4) {
		return -EINVAL;
	}

	elem_addr = strtoul(argv[1], NULL, 0);

	len = hex2bin(argv[2], label, sizeof(label));
	memset(label + len, 0, sizeof(label) - len);

	mod_id = strtoul(argv[3], NULL, 0);

	if (argc > 4) {
		cid = strtoul(argv[4], NULL, 0);
		err = bt_mesh_cfg_mod_sub_va_del_vnd(net.net_idx, net.dst,
						     elem_addr, label, mod_id,
						     cid, &sub_addr, &status);
	} else {
		err = bt_mesh_cfg_mod_sub_va_del(net.net_idx, net.dst,
						 elem_addr, label, mod_id,
						 &sub_addr, &status);
	}

	if (err) {
		printk("Unable to send Model Subscription Delete (err %d)\n",
		       err);
		return 0;
	}

	if (status) {
		printk("Model Subscription Delete failed with status 0x%02x\n",
		       status);
	} else {
		printk("0x%04x unsubscribed from Label UUID %s (va 0x%04x)\n",
		       elem_addr, argv[2], sub_addr);
	}

	return 0;
}

struct shell_cmd_help cmd_mod_sub_del_va_help = {
	NULL, "<elem addr> <Label UUID> <Model ID> [Company ID]", NULL
};

static int cmd_mod_sub_get(int argc,
			      char *argv[])
{
	uint16_t elem_addr, mod_id, cid;
	uint16_t subs[16];
	uint8_t status;
	size_t cnt;
	int err, i;

	elem_addr = strtoul(argv[1], NULL, 0);
	mod_id = strtoul(argv[2], NULL, 0);
	cnt = ARRAY_SIZE(subs);

	if (argc > 3) {
		cid = strtoul(argv[3], NULL, 0);
		err = bt_mesh_cfg_mod_sub_get_vnd(net.net_idx, net.dst,
						  elem_addr, mod_id, cid,
						  &status, subs, &cnt);
	} else {
		err = bt_mesh_cfg_mod_sub_get(net.net_idx, net.dst, elem_addr,
					      mod_id, &status, subs, &cnt);
	}

	if (err) {
		printk("Unable to send Model Subscription Get "
			    "(err %d)", err);
		return 0;
	}

	if (status) {
		printk("Model Subscription Get failed with "
			    "status 0x%02x", status);
	} else {
		printk(
			"Model Subscriptions for Element 0x%04x, "
			"Model 0x%04x %s:",
			elem_addr, mod_id, argc > 3 ? argv[3] : "(SIG)");

		if (!cnt) {
			printk("\tNone.");
		}

		for (i = 0; i < cnt; i++) {
			printk("\t0x%04x", subs[i]);
		}
	}

	return 0;
}

struct shell_cmd_help cmd_mod_sub_get_help = {
	NULL, "<elem addr> <Model ID> [Company ID]", NULL
};

static int mod_pub_get(uint16_t addr, uint16_t mod_id, uint16_t cid)
{
	struct bt_mesh_cfg_mod_pub pub;
	uint8_t status;
	int err;

	if (cid == CID_NVAL) {
		err = bt_mesh_cfg_mod_pub_get(net.net_idx, net.dst, addr,
					      mod_id, &pub, &status);
	} else {
		err = bt_mesh_cfg_mod_pub_get_vnd(net.net_idx, net.dst, addr,
						  mod_id, cid, &pub, &status);
	}

	if (err) {
		printk("Model Publication Get failed (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Model Publication Get failed (status 0x%02x)\n",
		       status);
		return 0;
	}

	printk("Model Publication for Element 0x%04x, Model 0x%04x:\n"
	       "\tPublish Address:                0x%04x\n"
	       "\tAppKeyIndex:                    0x%04x\n"
	       "\tCredential Flag:                %u\n"
	       "\tPublishTTL:                     %u\n"
	       "\tPublishPeriod:                  0x%02x\n"
	       "\tPublishRetransmitCount:         %u\n"
	       "\tPublishRetransmitInterval:      %ums\n",
	       addr, mod_id, pub.addr, pub.app_idx, pub.cred_flag, pub.ttl,
	       pub.period, BT_MESH_PUB_TRANSMIT_COUNT(pub.transmit),
	       BT_MESH_PUB_TRANSMIT_INT(pub.transmit));

	return 0;
}

static int mod_pub_set(uint16_t addr, uint16_t mod_id, uint16_t cid, char *argv[])
{
	struct bt_mesh_cfg_mod_pub pub;
	uint8_t status, count;
	uint16_t interval;
	int err;

	pub.addr = strtoul(argv[0], NULL, 0);
	pub.uuid = NULL;
	pub.app_idx = strtoul(argv[1], NULL, 0);
	pub.cred_flag = str2bool(argv[2]);
	pub.ttl = strtoul(argv[3], NULL, 0);
	pub.period = strtoul(argv[4], NULL, 0);

	count = strtoul(argv[5], NULL, 0);
	if (count > 7) {
		printk("Invalid retransmit count\n");
		return -EINVAL;
	}

	interval = strtoul(argv[6], NULL, 0);
	if (interval > (31 * 50) || (interval % 50)) {
		printk("Invalid retransmit interval %u\n", interval);
		return -EINVAL;
	}

	pub.transmit = BT_MESH_PUB_TRANSMIT(count, interval);

	if (cid == CID_NVAL) {
		err = bt_mesh_cfg_mod_pub_set(net.net_idx, net.dst, addr,
					      mod_id, &pub, &status);
	} else {
		err = bt_mesh_cfg_mod_pub_set_vnd(net.net_idx, net.dst, addr,
						  mod_id, cid, &pub, &status);
	}

	if (err) {
		printk("Model Publication Set failed (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Model Publication Set failed (status 0x%02x)\n",
		       status);
	} else {
		printk("Model Publication successfully set\n");
	}

	return 0;
}

static int cmd_mod_pub(int argc, char *argv[])
{
	uint16_t addr, mod_id, cid;

	if (argc < 3) {
		return -EINVAL;
	}

	addr = strtoul(argv[1], NULL, 0);
	mod_id = strtoul(argv[2], NULL, 0);

	argc -= 3;
	argv += 3;

	if (argc == 1 || argc == 8) {
		cid = strtoul(argv[0], NULL, 0);
		argc--;
		argv++;
	} else {
		cid = CID_NVAL;
	}

	if (argc > 0) {
		if (argc < 7) {
			return -EINVAL;
		}

		return mod_pub_set(addr, mod_id, cid, argv);
	} else {
		return mod_pub_get(addr, mod_id, cid);
	}
}

struct shell_cmd_help cmd_mod_pub_help = {
	NULL, "<addr> <mod id> [cid] [<PubAddr> "
	"<AppKeyIndex> <cred> <ttl> <period> <count> <interval>]" , NULL
};

static void hb_sub_print(struct bt_mesh_cfg_hb_sub *sub)
{
	printk("Heartbeat Subscription:\n"
	       "\tSource:      0x%04x\n"
	       "\tDestination: 0x%04x\n"
	       "\tPeriodLog:   0x%02x\n"
	       "\tCountLog:    0x%02x\n"
	       "\tMinHops:     %u\n"
	       "\tMaxHops:     %u\n",
	       sub->src, sub->dst, sub->period, sub->count,
	       sub->min, sub->max);
}

static int hb_sub_get(int argc, char *argv[])
{
	struct bt_mesh_cfg_hb_sub sub;
	uint8_t status;
	int err;

	err = bt_mesh_cfg_hb_sub_get(net.net_idx, net.dst, &sub, &status);
	if (err) {
		printk("Heartbeat Subscription Get failed (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Heartbeat Subscription Get failed (status 0x%02x)\n",
		       status);
	} else {
		hb_sub_print(&sub);
	}

	return 0;
}

static int hb_sub_set(int argc, char *argv[])
{
	struct bt_mesh_cfg_hb_sub sub;
	uint8_t status;
	int err;

	sub.src = strtoul(argv[1], NULL, 0);
	sub.dst = strtoul(argv[2], NULL, 0);
	sub.period = strtoul(argv[3], NULL, 0);

	err = bt_mesh_cfg_hb_sub_set(net.net_idx, net.dst, &sub, &status);
	if (err) {
		printk("Heartbeat Subscription Set failed (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Heartbeat Subscription Set failed (status 0x%02x)\n",
		       status);
	} else {
		hb_sub_print(&sub);
	}

	return 0;
}

static int cmd_hb_sub(int argc, char *argv[])
{
	if (argc > 1) {
		if (argc < 4) {
			return -EINVAL;
		}

		return hb_sub_set(argc, argv);
	} else {
		return hb_sub_get(argc, argv);
	}
}

struct shell_cmd_help cmd_hb_sub_help = {
	NULL, "<src> <dst> <period>", NULL
};

static int hb_pub_get(int argc, char *argv[])
{
	struct bt_mesh_cfg_hb_pub pub;
	uint8_t status;
	int err;

	err = bt_mesh_cfg_hb_pub_get(net.net_idx, net.dst, &pub, &status);
	if (err) {
		printk("Heartbeat Publication Get failed (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Heartbeat Publication Get failed (status 0x%02x)\n",
		       status);
		return 0;
	}

	printk("Heartbeat publication:\n");
	printk("\tdst 0x%04x count 0x%02x period 0x%02x\n",
	       pub.dst, pub.count, pub.period);
	printk("\tttl 0x%02x feat 0x%04x net_idx 0x%04x\n",
	       pub.ttl, pub.feat, pub.net_idx);

	return 0;
}

static int hb_pub_set(int argc, char *argv[])
{
	struct bt_mesh_cfg_hb_pub pub;
	uint8_t status;
	int err;

	pub.dst = strtoul(argv[1], NULL, 0);
	pub.count = strtoul(argv[2], NULL, 0);
	pub.period = strtoul(argv[3], NULL, 0);
	pub.ttl = strtoul(argv[4], NULL, 0);
	pub.feat = strtoul(argv[5], NULL, 0);
	pub.net_idx = strtoul(argv[5], NULL, 0);

	err = bt_mesh_cfg_hb_pub_set(net.net_idx, net.dst, &pub, &status);
	if (err) {
		printk("Heartbeat Publication Set failed (err %d)\n", err);
		return 0;
	}

	if (status) {
		printk("Heartbeat Publication Set failed (status 0x%02x)\n",
		       status);
	} else {
		printk("Heartbeat publication successfully set\n");
	}

	return 0;
}

static int cmd_hb_pub(int argc, char *argv[])
{
	if (argc > 1) {
		if (argc < 7) {
			return -EINVAL;
		}

		return hb_pub_set(argc, argv);
	} else {
		return hb_pub_get(argc, argv);
	}
}

struct shell_cmd_help cmd_hb_pub_help = {
	NULL, "<dst> <count> <period> <ttl> <features> <NetKeyIndex>" , NULL
};

#endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */

#if MYNEWT_VAL(BLE_MESH_PROV_DEVICE)
static int cmd_pb(bt_mesh_prov_bearer_t bearer, int argc, char *argv[])
{
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	if (str2bool(argv[1])) {
		err = bt_mesh_prov_enable(bearer);
		if (err) {
			printk("Failed to enable %s (err %d)\n",
			       bearer2str(bearer), err);
		} else {
			printk("%s enabled\n", bearer2str(bearer));
		}
	} else {
		err = bt_mesh_prov_disable(bearer);
		if (err) {
			printk("Failed to disable %s (err %d)\n",
			       bearer2str(bearer), err);
		} else {
			printk("%s disabled\n", bearer2str(bearer));
		}
	}

	return 0;

}

struct shell_cmd_help cmd_pb_help = {
	NULL, "<val: off, on>", NULL
};

#endif

#if MYNEWT_VAL(BLE_MESH_PB_ADV)
static int cmd_pb_adv(int argc, char *argv[])
{
	return cmd_pb(BT_MESH_PROV_ADV, argc, argv);
}

#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
static int cmd_provision_adv(int argc, char *argv[])
{
	uint8_t uuid[16];
	uint8_t attention_duration;
	uint16_t net_idx;
	uint16_t addr;
	size_t len;
	int err;

	len = hex2bin(argv[1], uuid, sizeof(uuid));
	(void)memset(uuid + len, 0, sizeof(uuid) - len);

	net_idx = strtoul(argv[2], NULL, 0);
	addr = strtoul(argv[3], NULL, 0);
	attention_duration = strtoul(argv[4], NULL, 0);

	err = bt_mesh_provision_adv(uuid, net_idx, addr, attention_duration);
	if (err) {
		printk("Provisioning failed (err %d)", err);
	}

	return 0;
}

struct shell_cmd_help cmd_provision_adv_help = {
	NULL, "<UUID> <NetKeyIndex> <addr> <AttentionDuration>" , NULL
};
#endif /* CONFIG_BT_MESH_PROVISIONER */

#endif /* CONFIG_BT_MESH_PB_ADV */

#if MYNEWT_VAL(BLE_MESH_PB_GATT)
static int cmd_pb_gatt(int argc, char *argv[])
{
	return cmd_pb(BT_MESH_PROV_GATT, argc, argv);
}
#endif /* CONFIG_BT_MESH_PB_GATT */

static int cmd_provision(int argc, char *argv[])
{
	const uint8_t *net_key = default_key;
	uint16_t net_idx, addr;
	uint32_t iv_index;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	net_idx = strtoul(argv[1], NULL, 0);
	addr = strtoul(argv[2], NULL, 0);

	if (argc > 3) {
		iv_index = strtoul(argv[3], NULL, 0);
	} else {
		iv_index = 0;
	}

	if (IS_ENABLED(CONFIG_BT_MESH_CDB)) {
		const struct bt_mesh_cdb_subnet *sub;

		sub = bt_mesh_cdb_subnet_get(net_idx);
		if (!sub) {
			printk("No cdb entry for subnet 0x%03x", net_idx);
			return 0;
		}

		net_key = sub->keys[SUBNET_KEY_TX_IDX(sub)].net_key;
	}

	err = bt_mesh_provision(net_key, net_idx, 0, iv_index, addr,
				default_key);
	if (err) {
		printk("Provisioning failed (err %d)\n", err);
	}

	return 0;
}

struct shell_cmd_help cmd_provision_help = {
	NULL, "<NetKeyIndex> <addr> [IVIndex]" , NULL
};

#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)

static int cmd_fault_get(int argc, char *argv[])
{
	uint8_t faults[32];
	size_t fault_count;
	uint8_t test_id;
	uint16_t cid;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	cid = strtoul(argv[1], NULL, 0);
	fault_count = sizeof(faults);

	err = bt_mesh_health_fault_get(net.dst, net.app_idx, cid, &test_id,
								   faults, &fault_count);
	if (err) {
		printk("Failed to send Health Fault Get (err %d)\n", err);
	} else {
		show_faults(test_id, cid, faults, fault_count);
	}

	return 0;
}

struct shell_cmd_help cmd_fault_get_help = {
	NULL, "<Company ID>", NULL
};

static int cmd_fault_clear(int argc, char *argv[])
{
	uint8_t faults[32];
	size_t fault_count;
	uint8_t test_id;
	uint16_t cid;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	cid = strtoul(argv[1], NULL, 0);
	fault_count = sizeof(faults);

	err = bt_mesh_health_fault_clear(net.dst, net.app_idx, cid,
									 &test_id, faults, &fault_count);
	if (err) {
		printk("Failed to send Health Fault Clear (err %d)\n", err);
	} else {
		show_faults(test_id, cid, faults, fault_count);
	}

	return 0;
}

struct shell_cmd_help cmd_fault_clear_help = {
	NULL, "<Company ID>", NULL
};

static int cmd_fault_clear_unack(int argc, char *argv[])
{
	uint16_t cid;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	cid = strtoul(argv[1], NULL, 0);

	err = bt_mesh_health_fault_clear(net.dst, net.app_idx, cid,
									 NULL, NULL, NULL);
	if (err) {
		printk("Health Fault Clear Unacknowledged failed (err %d)\n",
		       err);
	}

	return 0;
}

struct shell_cmd_help cmd_fault_clear_unack_help = {
	NULL, "<Company ID>", NULL
};

static int cmd_fault_test(int argc, char *argv[])
{
	uint8_t faults[32];
	size_t fault_count;
	uint8_t test_id;
	uint16_t cid;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	cid = strtoul(argv[1], NULL, 0);
	test_id = strtoul(argv[2], NULL, 0);
	fault_count = sizeof(faults);

	err = bt_mesh_health_fault_test(net.dst, net.app_idx, cid,
									test_id, faults, &fault_count);
	if (err) {
		printk("Failed to send Health Fault Test (err %d)\n", err);
	} else {
		show_faults(test_id, cid, faults, fault_count);
	}

	return 0;
}

struct shell_cmd_help cmd_fault_test_help = {
	NULL, "<Company ID> <Test ID>", NULL
};

static int cmd_fault_test_unack(int argc, char *argv[])
{
	uint16_t cid;
	uint8_t test_id;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	cid = strtoul(argv[1], NULL, 0);
	test_id = strtoul(argv[2], NULL, 0);

	err = bt_mesh_health_fault_test(net.dst, net.app_idx, cid,
									test_id, NULL, NULL);
	if (err) {
		printk("Health Fault Test Unacknowledged failed (err %d)\n",
		       err);
	}

	return 0;
}

struct shell_cmd_help cmd_fault_test_unack_help = {
	NULL, "<Company ID> <Test ID>", NULL
};

static int cmd_period_get(int argc, char *argv[])
{
	uint8_t divisor;
	int err;

	err = bt_mesh_health_period_get(net.dst, net.app_idx, &divisor);
	if (err) {
		printk("Failed to send Health Period Get (err %d)\n", err);
	} else {
		printk("Health FastPeriodDivisor: %u\n", divisor);
	}

	return 0;
}

static int cmd_period_set(int argc, char *argv[])
{
	uint8_t divisor, updated_divisor;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	divisor = strtoul(argv[1], NULL, 0);

	err = bt_mesh_health_period_set(net.dst, net.app_idx, divisor,
									&updated_divisor);
	if (err) {
		printk("Failed to send Health Period Set (err %d)\n", err);
	} else {
		printk("Health FastPeriodDivisor: %u\n", updated_divisor);
	}

	return 0;
}

struct shell_cmd_help cmd_period_set_help = {
	NULL, "<divisor>", NULL
};

static int cmd_period_set_unack(int argc, char *argv[])
{
	uint8_t divisor;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	divisor = strtoul(argv[1], NULL, 0);

	err = bt_mesh_health_period_set(net.dst, net.app_idx, divisor, NULL);
	if (err) {
		printk("Failed to send Health Period Set (err %d)\n", err);
	}

	return 0;
}

struct shell_cmd_help cmd_period_set_unack_help = {
	NULL, "<divisor>", NULL
};

static int cmd_attention_get(int argc, char *argv[])
{
	uint8_t attention;
	int err;

	err = bt_mesh_health_attention_get(net.dst, net.app_idx,
									   &attention);
	if (err) {
		printk("Failed to send Health Attention Get (err %d)\n", err);
	} else {
		printk("Health Attention Timer: %u\n", attention);
	}

	return 0;
}

static int cmd_attention_set(int argc, char *argv[])
{
	uint8_t attention, updated_attention;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	attention = strtoul(argv[1], NULL, 0);

	err = bt_mesh_health_attention_set(net.dst, net.app_idx, attention,
				 					   &updated_attention);
	if (err) {
		printk("Failed to send Health Attention Set (err %d)\n", err);
	} else {
		printk("Health Attention Timer: %u\n", updated_attention);
	}

	return 0;
}

struct shell_cmd_help cmd_attention_set_help = {
	NULL, "<timer>", NULL
};

static int cmd_attention_set_unack(int argc, char *argv[])
{
	uint8_t attention;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	attention = strtoul(argv[1], NULL, 0);

	err = bt_mesh_health_attention_set(net.dst, net.app_idx, attention,
									   NULL);
	if (err) {
		printk("Failed to send Health Attention Set (err %d)\n", err);
	}

	return 0;
}

struct shell_cmd_help cmd_attention_set_unack_help = {
	NULL, "<timer>", NULL
};

#endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */

static int cmd_add_fault(int argc, char *argv[])
{
	uint8_t fault_id;
	uint8_t i;

	if (argc < 2) {
		return -EINVAL;
	}

	fault_id = strtoul(argv[1], NULL, 0);
	if (!fault_id) {
		printk("The Fault ID must be non-zero!\n");
		return -EINVAL;
	}

	for (i = 0; i < sizeof(cur_faults); i++) {
		if (!cur_faults[i]) {
			cur_faults[i] = fault_id;
			break;
		}
	}

	if (i == sizeof(cur_faults)) {
		printk("Fault array is full. Use \"del-fault\" to clear it\n");
		return 0;
	}

	for (i = 0; i < sizeof(reg_faults); i++) {
		if (!reg_faults[i]) {
			reg_faults[i] = fault_id;
			break;
		}
	}

	if (i == sizeof(reg_faults)) {
		printk("No space to store more registered faults\n");
	}

	bt_mesh_fault_update(&elements[0]);

	return 0;
}

struct shell_cmd_help cmd_add_fault_help = {
	NULL, "<Fault ID>", NULL
};

static int cmd_del_fault(int argc, char *argv[])
{
	uint8_t fault_id;
	uint8_t i;

	if (argc < 2) {
		memset(cur_faults, 0, sizeof(cur_faults));
		printk("All current faults cleared\n");
		bt_mesh_fault_update(&elements[0]);
		return 0;
	}

	fault_id = strtoul(argv[1], NULL, 0);
	if (!fault_id) {
		printk("The Fault ID must be non-zero!\n");
		return -EINVAL;
	}

	for (i = 0; i < sizeof(cur_faults); i++) {
		if (cur_faults[i] == fault_id) {
			cur_faults[i] = 0;
			printk("Fault cleared\n");
		}
	}

	bt_mesh_fault_update(&elements[0]);

	return 0;
}

struct shell_cmd_help cmd_del_fault_help = {
	NULL, "[Fault ID]", NULL
};

#if MYNEWT_VAL(BLE_MESH_CDB)
struct shell_cmd_help cmd_cdb_create_help = {
	NULL, "[NetKey]", NULL
};
struct shell_cmd_help cmd_cdb_clear_help = {
	NULL, NULL, NULL
};
struct shell_cmd_help cmd_cdb_show_help = {
	NULL, NULL, NULL
};
struct shell_cmd_help cmd_cdb_node_add_help = {
	NULL, "<UUID> <addr> <num-elem> "
		  "<NetKeyIdx> [DevKey]", NULL
};
struct shell_cmd_help cmd_cdb_node_del_help = {
	NULL, "<addr>", NULL
};
struct shell_cmd_help cmd_cdb_subnet_add_help = {
	NULL, "<NeyKeyIdx> [<NetKey>]", NULL
};
struct shell_cmd_help cmd_cdb_subnet_del_help = {
	NULL, "<NetKeyIdx>", NULL
};
struct shell_cmd_help cmd_cdb_app_key_add_help = {
	NULL, "<NetKeyIdx> <AppKeyIdx> [<AppKey>]", NULL
};
struct shell_cmd_help cmd_cdb_app_key_del_help = {
	NULL, "<AppKeyIdx>", NULL
};

static int cmd_cdb_create(int argc, char *argv[])
{
	uint8_t net_key[16];
	size_t len;
	int err;

	if (argc < 2) {
		bt_rand(net_key, 16);
	} else {
		len = hex2bin(argv[1], net_key, sizeof(net_key));
		memset(net_key + len, 0, sizeof(net_key) - len);
	}

	err = bt_mesh_cdb_create(net_key);
	if (err < 0) {
		printk("Failed to create CDB (err %d)", err);
	}

	return 0;
}

static int cmd_cdb_clear(int argc, char *argv[])
{
	bt_mesh_cdb_clear();

	printk("Cleared CDB");

	return 0;
}

static void cdb_print_nodes(void)
{
	char key_hex_str[32 + 1], uuid_hex_str[32 + 1];
	struct bt_mesh_cdb_node *node;
	int i, total = 0;
	bool configured;

	printk("Address  Elements  Flags  %-32s  DevKey", "UUID");

	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); ++i) {
		node = &bt_mesh_cdb.nodes[i];
		if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
			continue;
		}

		configured = atomic_test_bit(node->flags,
					     BT_MESH_CDB_NODE_CONFIGURED);

		total++;
		bin2hex(node->uuid, 16, uuid_hex_str, sizeof(uuid_hex_str));
		bin2hex(node->dev_key, 16, key_hex_str, sizeof(key_hex_str));
		printk("0x%04x   %-8d  %-5s  %s  %s", node->addr,
			    node->num_elem, configured ? "C" : "-",
			    uuid_hex_str, key_hex_str);
	}

	printk("> Total nodes: %d", total);
}

static void cdb_print_subnets(void)
{
	struct bt_mesh_cdb_subnet *subnet;
	char key_hex_str[32 + 1];
	int i, total = 0;

	printk("NetIdx  NetKey");

	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
		subnet = &bt_mesh_cdb.subnets[i];
		if (subnet->net_idx == BT_MESH_KEY_UNUSED) {
			continue;
		}

		total++;
		bin2hex(subnet->keys[0].net_key, 16, key_hex_str,
			sizeof(key_hex_str));
		printk("0x%03x   %s", subnet->net_idx,
			    key_hex_str);
	}

	printk("> Total subnets: %d", total);
}

static void cdb_print_app_keys(void)
{
	struct bt_mesh_cdb_app_key *app_key;
	char key_hex_str[32 + 1];
	int i, total = 0;

	printk("NetIdx  AppIdx  AppKey");

	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i) {
		app_key = &bt_mesh_cdb.app_keys[i];
		if (app_key->net_idx == BT_MESH_KEY_UNUSED) {
			continue;
		}

		total++;
		bin2hex(app_key->keys[0].app_key, 16, key_hex_str,
			sizeof(key_hex_str));
		printk("0x%03x   0x%03x   %s",
			    app_key->net_idx, app_key->app_idx, key_hex_str);
	}

	printk("> Total app-keys: %d", total);
}

static int cmd_cdb_show(int argc, char *argv[])
{
	if (!atomic_test_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID)) {
		printk("No valid networks");
		return 0;
	}

	printk("Mesh Network Information");
	printk("========================");

	cdb_print_nodes();
	printk("---");
	cdb_print_subnets();
	printk("---");
	cdb_print_app_keys();

	return 0;
}

static int cmd_cdb_node_add(int argc, char *argv[])
{
	struct bt_mesh_cdb_node *node;
	uint8_t uuid[16], dev_key[16];
	uint16_t addr, net_idx;
	uint8_t num_elem;
	size_t len;

	len = hex2bin(argv[1], uuid, sizeof(uuid));
	memset(uuid + len, 0, sizeof(uuid) - len);

	addr = strtoul(argv[2], NULL, 0);
	num_elem = strtoul(argv[3], NULL, 0);
	net_idx = strtoul(argv[4], NULL, 0);

	if (argc < 6) {
		bt_rand(dev_key, 16);
	} else {
		len = hex2bin(argv[5], dev_key, sizeof(dev_key));
		memset(dev_key + len, 0, sizeof(dev_key) - len);
	}

	node = bt_mesh_cdb_node_alloc(uuid, addr, num_elem, net_idx);
	if (node == NULL) {
		printk("Failed to allocate node");
		return 0;
	}

	memcpy(node->dev_key, dev_key, 16);

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		bt_mesh_cdb_node_store(node);
	}

	printk("Added node 0x%04x", addr);

	return 0;
}

static int cmd_cdb_node_del(int argc, char *argv[])
{
	struct bt_mesh_cdb_node *node;
	uint16_t addr;

	addr = strtoul(argv[1], NULL, 0);

	node = bt_mesh_cdb_node_get(addr);
	if (node == NULL) {
		printk("No node with address 0x%04x", addr);
		return 0;
	}

	bt_mesh_cdb_node_del(node, true);

	printk("Deleted node 0x%04x", addr);

	return 0;
}

static int cmd_cdb_subnet_add(int argc,
			     char *argv[])
{
	struct bt_mesh_cdb_subnet *sub;
	uint8_t net_key[16];
	uint16_t net_idx;
	size_t len;

	net_idx = strtoul(argv[1], NULL, 0);

	if (argc < 3) {
		bt_rand(net_key, 16);
	} else {
		len = hex2bin(argv[2], net_key, sizeof(net_key));
		memset(net_key + len, 0, sizeof(net_key) - len);
	}

	sub = bt_mesh_cdb_subnet_alloc(net_idx);
	if (sub == NULL) {
		printk("Could not add subnet");
		return 0;
	}

	memcpy(sub->keys[0].net_key, net_key, 16);

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		bt_mesh_cdb_subnet_store(sub);
	}

	printk("Added Subnet 0x%03x", net_idx);

	return 0;
}

static int cmd_cdb_subnet_del(int argc,
			     char *argv[])
{
	struct bt_mesh_cdb_subnet *sub;
	uint16_t net_idx;

	net_idx = strtoul(argv[1], NULL, 0);

	sub = bt_mesh_cdb_subnet_get(net_idx);
	if (sub == NULL) {
		printk("No subnet with NetIdx 0x%03x", net_idx);
		return 0;
	}

	bt_mesh_cdb_subnet_del(sub, true);

	printk("Deleted subnet 0x%03x", net_idx);

	return 0;
}

static int cmd_cdb_app_key_add(int argc,
			      char *argv[])
{
	struct bt_mesh_cdb_app_key *key;
	uint16_t net_idx, app_idx;
	uint8_t app_key[16];
	size_t len;

	net_idx = strtoul(argv[1], NULL, 0);
	app_idx = strtoul(argv[2], NULL, 0);

	if (argc < 4) {
		bt_rand(app_key, 16);
	} else {
		len = hex2bin(argv[3], app_key, sizeof(app_key));
		memset(app_key + len, 0, sizeof(app_key) - len);
	}

	key = bt_mesh_cdb_app_key_alloc(net_idx, app_idx);
	if (key == NULL) {
		printk("Could not add AppKey");
		return 0;
	}

	memcpy(key->keys[0].app_key, app_key, 16);

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		bt_mesh_cdb_app_key_store(key);
	}

	printk("Added AppKey 0x%03x", app_idx);

	return 0;
}

static int cmd_cdb_app_key_del(int argc,
			      char *argv[])
{
	struct bt_mesh_cdb_app_key *key;
	uint16_t app_idx;

	app_idx = strtoul(argv[1], NULL, 0);

	key = bt_mesh_cdb_app_key_get(app_idx);
	if (key == NULL) {
		printk("No AppKey 0x%03x", app_idx);
		return 0;
	}

	bt_mesh_cdb_app_key_del(key, true);

	printk("Deleted AppKey 0x%03x", app_idx);

	return 0;
}
#endif

#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
static int cmd_gen_onoff_get(int argc, char *argv[])
{
	uint8_t state;
	int err;

	err = bt_mesh_gen_onoff_get(net.net_idx, net.dst, net.app_idx,
				    &state);
	if (err) {
		printk("Failed to send Generic OnOff Get (err %d)\n", err);
	} else {
		printk("Gen OnOff State %d\n", state);
	}

	return 0;
}

static int cmd_gen_onoff_set(int argc, char *argv[])
{
	uint8_t state;
	uint8_t val;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	val = strtoul(argv[1], NULL, 0);

	err = bt_mesh_gen_onoff_set(net.net_idx, net.dst, net.app_idx,
				    val, &state);
	if (err) {
		printk("Failed to send Generic OnOff Get (err %d)\n", err);
	} else {
		printk("Gen OnOff State %d\n", state);
	}

	return 0;
}

struct shell_cmd_help cmd_gen_onoff_set_help = {
	NULL, "<0|1>", NULL
};

static int cmd_gen_onoff_set_unack(int argc, char *argv[])
{
	uint8_t val;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	val = strtoul(argv[1], NULL, 0);

	err = bt_mesh_gen_onoff_set(net.net_idx, net.dst, net.app_idx,
				    val, NULL);
	if (err) {
		printk("Failed to send Generic OnOff Get (err %d)\n", err);
	}

	return 0;
}

struct shell_cmd_help cmd_gen_onoff_set_unack_help = {
	NULL, "<0|1>", NULL
};

static int cmd_gen_level_get(int argc, char *argv[])
{
	int16_t state;
	int err;

	err = bt_mesh_gen_level_get(net.net_idx, net.dst, net.app_idx,
				    &state);
	if (err) {
		printk("Failed to send Generic Level Get (err %d)\n", err);
	} else {
		printk("Gen Level State %d\n", state);
	}

	return 0;
}

static int cmd_gen_level_set(int argc, char *argv[])
{
	int16_t state;
	int16_t val;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	val = (int16_t)strtoul(argv[1], NULL, 0);

	err = bt_mesh_gen_level_set(net.net_idx, net.dst, net.app_idx,
				    val, &state);
	if (err) {
		printk("Failed to send Generic Level Get (err %d)\n", err);
	} else {
		printk("Gen Level State %d\n", state);
	}

	return 0;
}

struct shell_cmd_help cmd_gen_level_set_help = {
	NULL, "<level>", NULL
};

static int cmd_gen_level_set_unack(int argc, char *argv[])
{
	int16_t val;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	val = (int16_t)strtoul(argv[1], NULL, 0);

	err = bt_mesh_gen_level_set(net.net_idx, net.dst, net.app_idx,
				    val, NULL);
	if (err) {
		printk("Failed to send Generic Level Get (err %d)\n", err);
	}

	return 0;
}

struct shell_cmd_help cmd_gen_level_set_unack_help = {
	NULL, "<level>", NULL
};

#endif /* MYNEWT_VAL(BLE_MESH_SHELL_MODELS) */

static int cmd_print_credentials(int argc, char *argv[])
{
	bt_test_print_credentials();
	return 0;
}

static void print_comp_elem(struct bt_mesh_elem *elem,
			    bool primary)
{
	struct bt_mesh_model *mod;
	int i;

	printk("Loc: %u\n", elem->loc);
	printk("Model count: %u\n", elem->model_count);
	printk("Vnd model count: %u\n", elem->vnd_model_count);

	for (i = 0; i < elem->model_count; i++) {
		mod = &elem->models[i];
		printk("  Model: %u\n", i);
		printk("    ID: 0x%04x\n", mod->id);
		printk("    Opcode: 0x%08lx\n", mod->op->opcode);
	}

	for (i = 0; i < elem->vnd_model_count; i++) {
		mod = &elem->vnd_models[i];
		printk("  Vendor model: %u\n", i);
		printk("    Company: 0x%04x\n", mod->vnd.company);
		printk("    ID: 0x%04x\n", mod->vnd.id);
		printk("    Opcode: 0x%08lx\n", mod->op->opcode);
	}
}

static int cmd_print_composition_data(int argc, char *argv[])
{
	const struct bt_mesh_comp *comp;
	int i;

	comp = bt_mesh_comp_get();

	printk("CID: %u\n", comp->cid);
	printk("PID: %u\n", comp->pid);
	printk("VID: %u\n", comp->vid);

	for (i = 0; i < comp->elem_count; i++) {
		print_comp_elem(&comp->elem[i], i == 0);
	}

	return 0;
}


static const struct shell_cmd mesh_commands[] = {
    {
        .sc_cmd = "init",
        .sc_cmd_func = cmd_mesh_init,
        .help = NULL,
    },
#if MYNEWT_VAL(BLE_MESH_PB_ADV)
    {
        .sc_cmd = "pb-adv",
        .sc_cmd_func = cmd_pb_adv,
        .help = &cmd_pb_help,
    },
#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
    {
        .sc_cmd = "provision-adv",
        .sc_cmd_func = cmd_provision_adv,
        .help = &cmd_provision_adv_help,
    },
#endif
#endif
#if MYNEWT_VAL(BLE_MESH_PB_GATT)
    {
        .sc_cmd = "pb-gatt",
        .sc_cmd_func = cmd_pb_gatt,
        .help = &cmd_pb_help,
    },
#endif
    {
        .sc_cmd = "reset",
        .sc_cmd_func = cmd_reset,
        .help = &cmd_reset_help,
    },
    {
        .sc_cmd = "uuid",
        .sc_cmd_func = cmd_uuid,
        .help = &cmd_uuid_help,
    },
    {
        .sc_cmd = "input-num",
        .sc_cmd_func = cmd_input_num,
        .help = &cmd_input_num_help,
    },
    {
        .sc_cmd = "input-str",
        .sc_cmd_func = cmd_input_str,
        .help = &cmd_input_str_help,
    },
    {
        .sc_cmd = "static-oob",
        .sc_cmd_func = cmd_static_oob,
        .help = &cmd_static_oob_help,
    },
    {
        .sc_cmd = "provision",
        .sc_cmd_func = cmd_provision,
        .help = &cmd_provision_help,
    },
#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
    {
        .sc_cmd = "lpn",
        .sc_cmd_func = cmd_lpn,
        .help = &cmd_lpn_help,
    },
    {
        .sc_cmd = "poll",
        .sc_cmd_func = cmd_poll,
        .help = NULL,
    },
#endif
#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
    {
        .sc_cmd = "ident",
        .sc_cmd_func = cmd_ident,
        .help = NULL,
    },
#endif
    {
        .sc_cmd = "dst",
        .sc_cmd_func = cmd_dst,
        .help = &cmd_dst_help,
    },
    {
        .sc_cmd = "netidx",
        .sc_cmd_func = cmd_netidx,
        .help = &cmd_netidx_help,
    },
    {
        .sc_cmd = "appidx",
        .sc_cmd_func = cmd_appidx,
        .help = &cmd_appidx_help,
    },

    /* Commands which access internal APIs, for testing only */
    {
        .sc_cmd = "net-send",
        .sc_cmd_func = cmd_net_send,
        .help = &cmd_net_send_help,
    },
#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST)
    {
        .sc_cmd = "iv-update",
        .sc_cmd_func = cmd_iv_update,
        .help = NULL,
    },
    {
        .sc_cmd = "iv-update-test",
        .sc_cmd_func = cmd_iv_update_test,
        .help = &cmd_iv_update_test_help,
    },
#endif
    {
        .sc_cmd = "rpl-clear",
        .sc_cmd_func = cmd_rpl_clear,
        .help = NULL,
    },
#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
    {
        .sc_cmd = "lpn-subscribe",
        .sc_cmd_func = cmd_lpn_subscribe,
        .help = &cmd_lpn_subscribe_help,
    },
    {
        .sc_cmd = "lpn-unsubscribe",
        .sc_cmd_func = cmd_lpn_unsubscribe,
        .help = &cmd_lpn_unsubscribe_help,
    },
#endif
    {
        .sc_cmd = "print-credentials",
        .sc_cmd_func = cmd_print_credentials,
        .help = NULL,
    },
    {
        .sc_cmd = "print-composition-data",
        .sc_cmd_func = cmd_print_composition_data,
        .help = NULL,
    },


#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
    /* Configuration Client Model operations */
    {
        .sc_cmd = "timeout",
        .sc_cmd_func = cmd_timeout,
        .help = &cmd_timeout_help,
    },
    {
        .sc_cmd = "get-comp",
        .sc_cmd_func = cmd_get_comp,
        .help = &cmd_get_comp_help,
    },
    {
        .sc_cmd = "beacon",
        .sc_cmd_func = cmd_beacon,
        .help = &cmd_beacon_help,
    },
    {
        .sc_cmd = "beacon-listen",
        .sc_cmd_func = cmd_beacon_listen,
        .help = &cmd_beacon_listen_help,
    },
    {
        .sc_cmd = "ttl",
        .sc_cmd_func = cmd_ttl,
        .help = &cmd_ttl_help,
    },
    {
        .sc_cmd = "friend",
        .sc_cmd_func = cmd_friend,
        .help = &cmd_friend_help,
    },
    {
        .sc_cmd = "gatt-proxy",
        .sc_cmd_func = cmd_gatt_proxy,
        .help = &cmd_gatt_proxy_help,
    },
    {
        .sc_cmd = "relay",
        .sc_cmd_func = cmd_relay,
        .help = &cmd_relay_help,
    },
    {
        .sc_cmd = "net-key-add",
        .sc_cmd_func = cmd_net_key_add,
        .help = &cmd_net_key_add_help,
    },
    {
        .sc_cmd = "net-key-get",
        .sc_cmd_func = cmd_net_key_get,
        .help = &cmd_net_key_get_help,
    },
    {
        .sc_cmd = "net-key-del",
        .sc_cmd_func = cmd_net_key_del,
        .help = &cmd_net_key_del_help,
    },
    {
        .sc_cmd = "app-key-add",
        .sc_cmd_func = cmd_app_key_add,
        .help = &cmd_app_key_add_help,
    },
    {
        .sc_cmd = "app-key-del",
        .sc_cmd_func = cmd_app_key_del,
        .help = &cmd_app_key_del_help,
    },
    {
        .sc_cmd = "app-key-get",
        .sc_cmd_func = cmd_app_key_get,
        .help = &cmd_app_key_get_help,
    },
    {
        .sc_cmd = "net-transmit-param",
        .sc_cmd_func = cmd_net_transmit,
        .help = &cmd_net_transmit_help,
    },
    {
        .sc_cmd = "mod-app-bind",
        .sc_cmd_func = cmd_mod_app_bind,
        .help = &cmd_mod_app_bind_help,
    },
    {
        .sc_cmd = "mod-app-get",
        .sc_cmd_func = cmd_mod_app_get,
        .help = &cmd_mod_app_get_help,
    },
    {
        .sc_cmd = "mod-app-unbind",
        .sc_cmd_func = cmd_mod_app_unbind,
        .help = &cmd_mod_app_unbind_help,
    },
    {
        .sc_cmd = "mod-pub",
        .sc_cmd_func = cmd_mod_pub,
        .help = &cmd_mod_pub_help,
    },
    {
        .sc_cmd = "mod-sub-add",
        .sc_cmd_func = cmd_mod_sub_add,
        .help = &cmd_mod_sub_add_help,
    },
    {
        .sc_cmd = "mod-sub-del",
        .sc_cmd_func = cmd_mod_sub_del,
        .help = &cmd_mod_sub_del_help,
    },
    {
        .sc_cmd = "mod-sub-add-va",
        .sc_cmd_func = cmd_mod_sub_add_va,
        .help = &cmd_mod_sub_add_va_help,
    },
    {
        .sc_cmd = "mod-sub-get",
        .sc_cmd_func = cmd_mod_sub_get,
        .help = &cmd_mod_sub_get_help,
    },
    {
        .sc_cmd = "mod-sub-del-va",
        .sc_cmd_func = cmd_mod_sub_del_va,
        .help = &cmd_mod_sub_del_va_help,
    },
    {
        .sc_cmd = "hb-sub",
        .sc_cmd_func = cmd_hb_sub,
        .help = &cmd_hb_sub_help,
    },
    {
        .sc_cmd = "hb-pub",
        .sc_cmd_func = cmd_hb_pub,
        .help = &cmd_hb_pub_help,
    },
#endif

#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
    /* Health Client Model Operations */
    {
        .sc_cmd = "fault-get",
        .sc_cmd_func = cmd_fault_get,
        .help = &cmd_fault_get_help,
    },
    {
        .sc_cmd = "fault-clear",
        .sc_cmd_func = cmd_fault_clear,
        .help = &cmd_fault_clear_help,
    },
    {
        .sc_cmd = "fault-clear-unack",
        .sc_cmd_func = cmd_fault_clear_unack,
        .help = &cmd_fault_clear_unack_help,
    },
    {
        .sc_cmd = "fault-test",
        .sc_cmd_func = cmd_fault_test,
        .help = &cmd_fault_test_help,
    },
    {
        .sc_cmd = "fault-test-unack",
        .sc_cmd_func = cmd_fault_test_unack,
        .help = &cmd_fault_test_unack_help,
    },
    {
        .sc_cmd = "period-get",
        .sc_cmd_func = cmd_period_get,
        .help = NULL,
    },
    {
        .sc_cmd = "period-set",
        .sc_cmd_func = cmd_period_set,
        .help = &cmd_period_set_help,
    },
    {
        .sc_cmd = "period-set-unack",
        .sc_cmd_func = cmd_period_set_unack,
        .help = &cmd_period_set_unack_help,
    },
    {
        .sc_cmd = "attention-get",
        .sc_cmd_func = cmd_attention_get,
        .help = NULL,
    },
    {
        .sc_cmd = "attention-set",
        .sc_cmd_func = cmd_attention_set,
        .help = &cmd_attention_set_help,
    },
    {
        .sc_cmd = "attention-set-unack",
        .sc_cmd_func = cmd_attention_set_unack,
        .help = &cmd_attention_set_unack_help,
    },
#endif

    /* Health Server Model Operations */
    {
        .sc_cmd = "add-fault",
        .sc_cmd_func = cmd_add_fault,
        .help = &cmd_add_fault_help,
    },
    {
        .sc_cmd = "del-fault",
        .sc_cmd_func = cmd_del_fault,
        .help = &cmd_del_fault_help,
    },

#if MYNEWT_VAL(BLE_MESH_CDB)
	{
        .sc_cmd = "cdb-create",
        .sc_cmd_func = cmd_cdb_create,
        .help = &cmd_cdb_create_help,
    },{
        .sc_cmd = "cdb-clear",
        .sc_cmd_func = cmd_cdb_clear,
        .help = &cmd_cdb_clear_help,
    },{
        .sc_cmd = "cdb-show",
        .sc_cmd_func = cmd_cdb_show,
        .help = &cmd_cdb_show_help,
    },{
        .sc_cmd = "cdb-node-add",
        .sc_cmd_func = cmd_cdb_node_add,
        .help = &cmd_cdb_node_add_help,
    },{
        .sc_cmd = "cdb-node-del",
        .sc_cmd_func = cmd_cdb_node_del,
        .help = &cmd_cdb_node_del_help,
    },{
        .sc_cmd = "cdb-subnet-add",
        .sc_cmd_func = cmd_cdb_subnet_add,
        .help = &cmd_cdb_subnet_add_help,
    },{
        .sc_cmd = "cdb-subnet-del",
        .sc_cmd_func = cmd_cdb_subnet_del,
        .help = &cmd_cdb_subnet_del_help,
    },{
        .sc_cmd = "cdb-app-key-add",
        .sc_cmd_func = cmd_cdb_app_key_add,
        .help = &cmd_cdb_app_key_add_help,
    },{
        .sc_cmd = "cdb-app-key-add",
        .sc_cmd_func = cmd_cdb_app_key_del,
        .help = &cmd_cdb_app_key_del_help,
    },
#endif

#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
    /* Generic Client Model Operations */
    {
        .sc_cmd = "gen-onoff-get",
        .sc_cmd_func = cmd_gen_onoff_get,
        .help = NULL,
    },
    {
        .sc_cmd = "gen-onoff-set",
        .sc_cmd_func = cmd_gen_onoff_set,
        .help = &cmd_gen_onoff_set_help,
    },
    {
        .sc_cmd = "gen-onoff-set-unack",
        .sc_cmd_func = cmd_gen_onoff_set_unack,
        .help = &cmd_gen_onoff_set_unack_help,
    },
    {
        .sc_cmd = "gen-level-get",
        .sc_cmd_func = cmd_gen_level_get,
        .help = NULL,
    },
    {
        .sc_cmd = "gen-level-set",
        .sc_cmd_func = cmd_gen_level_set,
        .help = &cmd_gen_level_set_help,
    },
    {
        .sc_cmd = "gen-level-set-unack",
        .sc_cmd_func = cmd_gen_level_set_unack,
        .help = &cmd_gen_level_set_unack_help,
    },
#endif

    { 0 },
};

static void mesh_shell_thread(void *args)
{
	while (1) {
		os_eventq_run(&mesh_shell_queue);
	}
}

static void bt_mesh_shell_task_init(void)
{
	os_eventq_init(&mesh_shell_queue);

	os_task_init(&mesh_shell_task, "mesh_sh", mesh_shell_thread, NULL,
		     BLE_MESH_SHELL_TASK_PRIO, OS_WAIT_FOREVER, g_blemesh_shell_stack,
		     BLE_MESH_SHELL_STACK_SIZE);
}
#endif

void ble_mesh_shell_init(void)
{
#if (MYNEWT_VAL(BLE_MESH_SHELL))

	/* Initialize health pub message */
	health_pub_init();

	/* Shell and other mesh clients should use separate task to
	   avoid deadlocks with mesh message processing queue */
	bt_mesh_shell_task_init();
	shell_evq_set(&mesh_shell_queue);
	shell_register("mesh", mesh_commands);

#endif
}
