summaryrefslogtreecommitdiff
path: root/lib/bt/esp_ble_mesh/models/server
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
committerjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
commitee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch)
tree8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/esp_ble_mesh/models/server
parent239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff)
downloadtangara-fw-ee29c25b29eaa4fac4e897442634b69ecc8d8125.tar.gz
Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
Diffstat (limited to 'lib/bt/esp_ble_mesh/models/server')
-rw-r--r--lib/bt/esp_ble_mesh/models/server/generic_server.c2864
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/generic_server.h367
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/lighting_server.h510
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/sensor_server.h246
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/server_common.h124
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/state_binding.h108
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/state_transition.h100
-rw-r--r--lib/bt/esp_ble_mesh/models/server/include/mesh/time_scene_server.h384
-rw-r--r--lib/bt/esp_ble_mesh/models/server/lighting_server.c3597
-rw-r--r--lib/bt/esp_ble_mesh/models/server/sensor_server.c1173
-rw-r--r--lib/bt/esp_ble_mesh/models/server/server_common.c248
-rw-r--r--lib/bt/esp_ble_mesh/models/server/state_binding.c336
-rw-r--r--lib/bt/esp_ble_mesh/models/server/state_transition.c1040
-rw-r--r--lib/bt/esp_ble_mesh/models/server/time_scene_server.c1536
14 files changed, 12633 insertions, 0 deletions
diff --git a/lib/bt/esp_ble_mesh/models/server/generic_server.c b/lib/bt/esp_ble_mesh/models/server/generic_server.c
new file mode 100644
index 00000000..747d78b2
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/generic_server.c
@@ -0,0 +1,2864 @@
+/* Bluetooth: Mesh Generic Server Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+
+#include "btc_ble_mesh_generic_model.h"
+
+#include "mesh/config.h"
+#include "access.h"
+#include "transport.h"
+#include "mesh/model_opcode.h"
+#include "mesh/state_transition.h"
+#include "mesh/device_property.h"
+
+#if CONFIG_BLE_MESH_GENERIC_SERVER
+
+static bt_mesh_mutex_t generic_server_lock;
+
+void bt_mesh_generic_server_lock(void)
+{
+ bt_mesh_mutex_lock(&generic_server_lock);
+}
+
+void bt_mesh_generic_server_unlock(void)
+{
+ bt_mesh_mutex_unlock(&generic_server_lock);
+}
+
+/* message handlers (Start) */
+
+/* Generic OnOff Server message handlers */
+static void send_gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish)
+{
+ struct bt_mesh_gen_onoff_srv *srv = model->user_data;
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 3;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS);
+ net_buf_simple_add_u8(msg, srv->state.onoff);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_u8(msg, srv->state.target_onoff);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_onoff_srv *srv = model->user_data;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ send_gen_onoff_status(model, ctx, false);
+}
+
+void gen_onoff_publish(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ send_gen_onoff_status(model, NULL, true);
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_onoff_srv *srv = model->user_data;
+ uint8_t tid = 0U, onoff = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ onoff = net_buf_simple_pull_u8(buf);
+ if (onoff > BLE_MESH_STATE_ON) {
+ BT_ERR("Invalid OnOff value 0x%02x", onoff);
+ return;
+ }
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .onoff_set.op_en = optional,
+ .onoff_set.onoff = onoff,
+ .onoff_set.tid = tid,
+ .onoff_set.trans_time = trans_time,
+ .onoff_set.delay = delay,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ONOFF_SET) {
+ send_gen_onoff_status(model, ctx, false);
+ }
+ send_gen_onoff_status(model, NULL, true);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state.target_onoff = onoff;
+
+ if (srv->state.target_onoff != srv->state.onoff) {
+ generic_onoff_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_onoff_set.onoff = srv->state.onoff,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ONOFF_SET) {
+ send_gen_onoff_status(model, ctx, false);
+ }
+ send_gen_onoff_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state.onoff = srv->state.target_onoff;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ONOFF_SET) {
+ send_gen_onoff_status(model, ctx, false);
+ }
+ send_gen_onoff_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+/* Generic Level Server message handlers */
+static void send_gen_level_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish)
+{
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 5;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+ net_buf_simple_add_le16(msg, srv->state.level);
+ if (srv->transition.counter) {
+ if (srv->state.move_start) {
+ if (srv->state.positive) {
+ net_buf_simple_add_le16(msg, INT16_MAX);
+ } else { /* 0 should not be possible */
+ net_buf_simple_add_le16(msg, INT16_MIN);
+ }
+ net_buf_simple_add_u8(msg, BLE_MESH_UNKNOWN_REMAIN_TIME);
+ } else {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state.target_level);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_level_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ send_gen_level_status(model, ctx, false);
+}
+
+void gen_level_publish(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ send_gen_level_status(model, NULL, true);
+}
+
+static void gen_level_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ int16_t level = 0;
+ int64_t now = 0;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ level = (int16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .level_set.op_en = optional,
+ .level_set.level = level,
+ .level_set.tid = tid,
+ .level_set.trans_time = trans_time,
+ .level_set.delay = delay,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_LEVEL_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state.target_level = level;
+
+ /**
+ * If the target state is equal to the current state, the transition
+ * shall not be started and is considered complete.
+ */
+ if (srv->state.target_level != srv->state.level) {
+ generic_level_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_level_set.level = srv->state.level,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_LEVEL_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state.level = srv->state.target_level;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_LEVEL_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void gen_delta_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ int32_t tmp32 = 0, delta = 0;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ delta = (int32_t)net_buf_simple_pull_le32(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .delta_set.op_en = optional,
+ .delta_set.delta_level = delta,
+ .delta_set.tid = tid,
+ .delta_set.trans_time = trans_time,
+ .delta_set.delay = delay,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ /**
+ * A number of Generic Delta Set and Generic Delta Set Unacknowledged
+ * messages with the same transaction identifier set in the TID field
+ * may be sent.
+ *
+ * A new transaction starts when the TID field value in the received
+ * message is different from the TID field value in the previously
+ * received message that was using the same source and destination
+ * addresses or from the most recently received message with the same
+ * TID field value that was received 6 or more seconds earlier.
+ */
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (srv->state.last_delta == delta) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_DELTA_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ tmp32 = srv->state.last_level + delta;
+ } else {
+ /* Starts a new transaction */
+ srv->state.last_level = srv->state.level;
+ tmp32 = srv->state.level + delta;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state.last_delta = delta;
+ if (tmp32 < INT16_MIN) {
+ tmp32 = INT16_MIN;
+ } else if (tmp32 > INT16_MAX) {
+ tmp32 = INT16_MAX;
+ }
+ srv->state.target_level = tmp32;
+
+ /**
+ * If the target state is equal to the current state, the transition
+ * shall not be started and is considered complete.
+ */
+ if (srv->state.target_level != srv->state.level) {
+ generic_level_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_delta_set.level = srv->state.level,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_DELTA_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state.level = srv->state.target_level;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_DELTA_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void gen_move_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ int16_t delta = 0;
+ int32_t tmp32 = 0;
+ int64_t now = 0;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ delta = (int16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .move_set.op_en = optional,
+ .move_set.delta_level = delta,
+ .move_set.tid = tid,
+ .move_set.trans_time = trans_time,
+ .move_set.delay = delay,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_MOVE_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state.last_delta = delta;
+
+ tmp32 = srv->state.level + delta;
+ if (tmp32 < INT16_MIN) {
+ tmp32 = INT16_MIN;
+ } else if (tmp32 > INT16_MAX) {
+ tmp32 = INT16_MAX;
+ }
+ srv->state.target_level = tmp32;
+
+ /**
+ * If the target state is equal to the current state, the transition
+ * shall not be started and is considered complete.
+ */
+ if (srv->state.target_level != srv->state.level) {
+ generic_level_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_move_set.level = srv->state.level,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_MOVE_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+ srv->state.move_start = false;
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ if (delta) {
+ srv->state.move_start = true;
+ srv->state.positive = (delta > 0) ? true : false;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_MOVE_SET) {
+ send_gen_level_status(model, ctx, false);
+ }
+ send_gen_level_status(model, NULL, true);
+
+ bt_mesh_generic_server_unlock();
+
+ /**
+ * If (trans_time == 0) OR (delta == 0)
+ * 1. If the resulting Transition Time is equal to 0 or is undefined,
+ * the Generic Move Set command will not initiate any Generic Level
+ * state change.
+ * 2. When a Generic Level Server receives the message with a value of
+ * the Delta Level field equal to 0, it shall stop changing the
+ * Generic Level state. (if delta == 0, srv->state.target_level will
+ * equal to srv->state.level)
+ */
+ if (srv->transition.counter == 0U) {
+ srv->state.move_start = false;
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_move_set.level = srv->state.level,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ return;
+ }
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+/* Generic Default Transition Time Server message handlers */
+static void send_gen_def_trans_time_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish)
+{
+ struct bt_mesh_gen_def_trans_time_srv *srv = model->user_data;
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 1;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS);
+ net_buf_simple_add_u8(msg, srv->state.trans_time);
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_def_trans_time_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_def_trans_time_srv *srv = model->user_data;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ send_gen_def_trans_time_status(model, ctx, false);
+}
+
+static void gen_def_trans_time_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_def_trans_time_srv *srv = model->user_data;
+ uint8_t trans_time = 0U;
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ trans_time = net_buf_simple_pull_u8(buf);
+ if ((trans_time & 0x3F) == 0x3F) {
+ BT_WARN("Invalid Transaction Number of Steps 0x3f");
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .def_trans_time_set.trans_time = trans_time,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (srv->state.trans_time != trans_time) {
+ srv->state.trans_time = trans_time;
+ }
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_def_trans_time_set.trans_time = trans_time,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET) {
+ send_gen_def_trans_time_status(model, ctx, false);
+ }
+ send_gen_def_trans_time_status(model, NULL, true);
+}
+
+/* Generic Power OnOff Server message handlers */
+static void send_gen_onpowerup_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 1;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS);
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV: {
+ struct bt_mesh_gen_power_onoff_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->onpowerup);
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV: {
+ struct bt_mesh_gen_power_onoff_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->onpowerup);
+ break;
+ }
+ default:
+ BT_ERR("Invalid Generic Power OnOff Server 0x%04x", model->id);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_onpowerup_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_power_onoff_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ send_gen_onpowerup_status(model, ctx, false);
+}
+
+/* Generic Power OnOff Setup Server message handlers */
+void gen_onpowerup_publish(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV: {
+ struct bt_mesh_gen_power_onoff_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power OnOff Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV: {
+ struct bt_mesh_gen_power_onoff_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power OnOff Setup Server state");
+ return;
+ }
+ break;
+ }
+ default:
+ BT_ERR("Invalid Generic Power OnOff Server 0x%04x", model->id);
+ return;
+ }
+
+ send_gen_onpowerup_status(model, NULL, true);
+}
+
+static void gen_onpowerup_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_power_onoff_setup_srv *srv = model->user_data;
+ uint8_t onpowerup = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ onpowerup = net_buf_simple_pull_u8(buf);
+ if (onpowerup > BLE_MESH_STATE_RESTORE) {
+ BT_WARN("Invalid OnPowerUp value 0x%02x", onpowerup);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .onpowerup_set.onpowerup = onpowerup,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (srv->state->onpowerup != onpowerup) {
+ srv->state->onpowerup = onpowerup;
+ }
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_onpowerup_set.onpowerup = onpowerup,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET) {
+ send_gen_onpowerup_status(model, ctx, false);
+ }
+ send_gen_onpowerup_status(model, NULL, true);
+}
+
+/* Generic Power Level Server message handlers */
+static void send_gen_power_level_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 5;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS:
+ case BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS: {
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ if (opcode == BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS) {
+ net_buf_simple_add_le16(msg, srv->state->power_actual);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state->target_power_actual);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ } else if (opcode == BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS) {
+ net_buf_simple_add_le16(msg, srv->state->power_last);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV) {
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->power_default);
+ } else if (model->id == BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV) {
+ struct bt_mesh_gen_power_level_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->power_default);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV) {
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->power_range_min);
+ net_buf_simple_add_le16(msg, srv->state->power_range_max);
+ } else if (model->id == BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV) {
+ struct bt_mesh_gen_power_level_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->power_range_min);
+ net_buf_simple_add_le16(msg, srv->state->power_range_max);
+ }
+ break;
+ default:
+ BT_WARN("Unknown Generic Power status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_power_level_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ uint16_t opcode = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET:
+ opcode = BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET:
+ opcode = BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET:
+ opcode = BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET:
+ opcode = BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Generic Power Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_gen_power_level_status(model, ctx, false, opcode);
+}
+
+void gen_power_level_publish(struct bt_mesh_model *model, uint16_t opcode)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV: {
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power Level Server state");
+ return;
+ }
+ break;
+ }
+ case ESP_BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV: {
+ struct bt_mesh_gen_power_level_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power Level Setup Server state");
+ return;
+ }
+ break;
+ }
+ default:
+ BT_ERR("Invalid Generic Power Level Server 0x%04x", model->id);
+ return;
+ }
+
+ send_gen_power_level_status(model, NULL, true, opcode);
+}
+
+static void gen_power_level_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ uint16_t power = 0U;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ power = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .power_level_set.op_en = optional,
+ .power_level_set.power = power,
+ .power_level_set.tid = tid,
+ .power_level_set.trans_time = trans_time,
+ .power_level_set.delay = delay,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET) {
+ send_gen_power_level_status(model, ctx, false, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+ }
+ send_gen_power_level_status(model, NULL, true, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ if (power) {
+ if (srv->state->power_range_min && power < srv->state->power_range_min) {
+ power = srv->state->power_range_min;
+ } else if (srv->state->power_range_max && power > srv->state->power_range_max) {
+ power = srv->state->power_range_max;
+ }
+ }
+ srv->state->target_power_actual = power;
+
+ /* If the target state is equal to the current state, the transition
+ * shall not be started and is considered complete.
+ */
+ if (srv->state->target_power_actual != srv->state->power_actual) {
+ generic_power_level_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_power_level_set.power = srv->state->power_actual,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET) {
+ send_gen_power_level_status(model, ctx, false, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+ }
+ send_gen_power_level_status(model, NULL, true, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->power_actual = srv->state->target_power_actual;
+ /* Whenever the Generic Power Actual state is changed to a non-zero value
+ * as a result of a non-transactional message or a completed sequence of
+ * transactional messages, the value of the Generic Power Last state shall
+ * be set to the value of the Generic Power Actual state.
+ */
+ if (srv->state->power_actual) {
+ srv->state->power_last = srv->state->power_actual;
+ }
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET) {
+ send_gen_power_level_status(model, ctx, false, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+ }
+ send_gen_power_level_status(model, NULL, true, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+
+ bt_mesh_generic_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+/* Generic Power Level Setup Server message handlers */
+static void gen_power_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_power_level_setup_srv *srv = model->user_data;
+ uint16_t power = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ power = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .power_default_set.power = power, /* Just callback the actual received value */
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ /**
+ * Value 0x0000 has a special meaning defined: use the value of the
+ * Generic Power Last state as the default value.
+ */
+ if (power == 0x0000) {
+ power = srv->state->power_last;
+ }
+
+ if (srv->state->power_default != power) {
+ srv->state->power_default = power;
+ }
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_power_default_set.power = power,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET) {
+ send_gen_power_level_status(model, ctx, false, BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS);
+ }
+ send_gen_power_level_status(model, NULL, true, BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS);
+}
+
+static void gen_power_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_power_level_setup_srv *srv = model->user_data;
+ uint16_t range_min = 0U, range_max = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ range_min = net_buf_simple_pull_le16(buf);
+ range_max = net_buf_simple_pull_le16(buf);
+
+ if (range_min > range_max) {
+ BT_ERR("Range min 0x%04x is greater than range max 0x%04x",
+ range_min, range_max);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .power_range_set.range_min = range_min,
+ .power_range_set.range_max = range_max,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (range_min == 0x0000) {
+ srv->state->status_code = BLE_MESH_CANNOT_SET_RANGE_MIN;
+ } else if (range_max == 0x0000) {
+ srv->state->status_code = BLE_MESH_CANNOT_SET_RANGE_MAX;
+ } else {
+ srv->state->status_code = BLE_MESH_RANGE_UPDATE_SUCCESS;
+ }
+
+ if (range_min && srv->state->power_range_min != range_min) {
+ srv->state->power_range_min = range_min;
+ }
+
+ if (range_max && srv->state->power_range_max != range_max) {
+ srv->state->power_range_max = range_max;
+ }
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_power_range_set.range_min = srv->state->power_range_min,
+ .gen_power_range_set.range_max = srv->state->power_range_max,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET) {
+ send_gen_power_level_status(model, ctx, false, BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS);
+ }
+ send_gen_power_level_status(model, NULL, true, BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS);
+}
+
+/* Generic Battery Server message handlers */
+static void gen_battery_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_battery_srv *srv = model->user_data;
+ NET_BUF_SIMPLE_DEFINE(msg, 2 + 8 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+
+ if (srv == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ bt_mesh_model_msg_init(&msg, BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS);
+ net_buf_simple_add_le32(&msg, srv->state.time_to_discharge << 8 | srv->state.battery_level);
+ net_buf_simple_add_le32(&msg, srv->state.battery_flags << 24 | srv->state.time_to_charge);
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, &msg, NULL, NULL));
+}
+
+/* Generic Location Server message handlers */
+static void send_gen_location_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 1 + 10;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_GEN_LOCATION_SRV) {
+ struct bt_mesh_gen_location_srv *srv = model->user_data;
+ net_buf_simple_add_le32(msg, srv->state->global_latitude);
+ net_buf_simple_add_le32(msg, srv->state->global_longitude);
+ net_buf_simple_add_le16(msg, srv->state->global_altitude);
+ } else if (model->id == BLE_MESH_MODEL_ID_GEN_LOCATION_SETUP_SRV) {
+ struct bt_mesh_gen_location_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le32(msg, srv->state->global_latitude);
+ net_buf_simple_add_le32(msg, srv->state->global_longitude);
+ net_buf_simple_add_le16(msg, srv->state->global_altitude);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_GEN_LOCATION_SRV) {
+ struct bt_mesh_gen_location_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->local_north);
+ net_buf_simple_add_le16(msg, srv->state->local_east);
+ net_buf_simple_add_le16(msg, srv->state->local_altitude);
+ net_buf_simple_add_u8(msg, srv->state->floor_number);
+ net_buf_simple_add_le16(msg, srv->state->uncertainty);
+ } else if (model->id == BLE_MESH_MODEL_ID_GEN_LOCATION_SETUP_SRV) {
+ struct bt_mesh_gen_location_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->local_north);
+ net_buf_simple_add_le16(msg, srv->state->local_east);
+ net_buf_simple_add_le16(msg, srv->state->local_altitude);
+ net_buf_simple_add_u8(msg, srv->state->floor_number);
+ net_buf_simple_add_le16(msg, srv->state->uncertainty);
+ }
+ break;
+ default:
+ BT_WARN("Unknown Generic Location status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_location_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_location_srv *srv = model->user_data;
+ uint16_t opcode = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET:
+ opcode = BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET:
+ opcode = BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Generic Location Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_gen_location_status(model, ctx, false, opcode);
+}
+
+/* Generic Location Setup Server message handlers */
+static void gen_location_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_location_setup_srv *srv = model->user_data;
+ uint16_t opcode = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET:
+ case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK: {
+ opcode = BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS;
+ int32_t latitude = net_buf_simple_pull_le32(buf);
+ int32_t longitude = net_buf_simple_pull_le32(buf);
+ int16_t altitude = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .loc_global_set.latitude = latitude,
+ .loc_global_set.longitude = longitude,
+ .loc_global_set.altitude = altitude,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (latitude != 0x80000000) {
+ srv->state->global_latitude = latitude;
+ }
+ if (longitude != 0x80000000) {
+ srv->state->global_longitude = longitude;
+ }
+ if (altitude != 0x7FFF) {
+ srv->state->global_altitude = altitude;
+ }
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_loc_global_set.latitude = srv->state->global_latitude,
+ .gen_loc_global_set.longitude = srv->state->global_longitude,
+ .gen_loc_global_set.altitude = srv->state->global_altitude,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ break;
+ }
+ case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET:
+ case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK: {
+ opcode = BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS;
+ uint16_t north = net_buf_simple_pull_le16(buf);
+ uint16_t east = net_buf_simple_pull_le16(buf);
+ uint16_t altitude = net_buf_simple_pull_le16(buf);
+ uint8_t floor = net_buf_simple_pull_u8(buf);
+ uint16_t uncertainty = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .loc_local_set.north = north,
+ .loc_local_set.east = east,
+ .loc_local_set.altitude = altitude,
+ .loc_local_set.floor_number = floor,
+ .loc_local_set.uncertainty = uncertainty,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (north != 0x8000) {
+ srv->state->local_north = north;
+ }
+ if (east != 0x8000) {
+ srv->state->local_east = east;
+ }
+ if (altitude != 0x7FFF) {
+ srv->state->local_altitude = altitude;
+ }
+ if (floor != 0xFF) {
+ srv->state->floor_number = floor;
+ }
+ srv->state->uncertainty = uncertainty;
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_loc_local_set.north = srv->state->local_north,
+ .gen_loc_local_set.east = srv->state->local_east,
+ .gen_loc_local_set.altitude = srv->state->local_altitude,
+ .gen_loc_local_set.floor_number = srv->state->floor_number,
+ .gen_loc_local_set.uncertainty = srv->state->uncertainty,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ break;
+ }
+ default:
+ BT_WARN("Unknown Generic Location Set opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET ||
+ ctx->recv_op == BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET) {
+ send_gen_location_status(model, ctx, false, opcode);
+ }
+ send_gen_location_status(model, NULL, true, opcode);
+}
+
+/* Generic User Property Server message handlers */
+static struct bt_mesh_generic_property *gen_get_user_property(struct bt_mesh_model *model,
+ uint16_t property_id)
+{
+ struct bt_mesh_gen_user_prop_srv *srv = model->user_data;
+ int i;
+
+ for (i = 0; i < srv->property_count; i++) {
+ if (srv->properties[i].id == property_id) {
+ return &srv->properties[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void send_gen_user_prop_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t property_id, bool publish)
+{
+ struct bt_mesh_generic_property *property = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t length = 0U;
+
+ if (property_id == BLE_MESH_INVALID_DEVICE_PROPERTY_ID) {
+ BT_ERR("Invalid User Property ID 0x%04x", property_id);
+ return;
+ }
+
+ property = gen_get_user_property(model, property_id);
+ if (property == NULL) {
+ BT_WARN("User property 0x%04x not exists", property_id);
+ length = sizeof(property_id);
+ } else {
+ length = sizeof(property->id) + sizeof(property->user_access) + property->val->len;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, 1 + length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS);
+ if (property == NULL) {
+ net_buf_simple_add_le16(msg, property_id);
+ } else {
+ net_buf_simple_add_le16(msg, property->id);
+ net_buf_simple_add_u8(msg, property->user_access);
+ if ((ctx->recv_op == BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET &&
+ property->user_access != USER_ACCESS_PROHIBIT &&
+ property->user_access != USER_ACCESS_WRITE) ||
+ ((ctx->recv_op == BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET ||
+ ctx->recv_op == BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK) &&
+ property->user_access != USER_ACCESS_PROHIBIT &&
+ property->user_access != USER_ACCESS_READ)) {
+ net_buf_simple_add_mem(msg, property->val->data, property->val->len);
+ }
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_user_prop_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_user_prop_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ /**
+ * Also we can use __attribute__((packed)) for esp_ble_mesh_gen_user_property_get_t,
+ * and directly callback with buf->data & buf->len.
+ */
+ bt_mesh_gen_server_recv_get_msg_t get = {0};
+ const uint8_t *param = NULL;
+ size_t len = 0;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET) {
+ get.user_property_get.id = net_buf_simple_pull_le16(buf);
+ param = (const uint8_t *)&get;
+ len = sizeof(get);
+ }
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, param, len);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET: {
+ struct net_buf_simple *msg = NULL;
+ uint8_t i;
+ msg = bt_mesh_alloc_buf(1 + srv->property_count * 2 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS);
+ for (i = 0U; i < srv->property_count; i++) {
+ if (srv->properties[i].admin_access != ADMIN_NOT_USER_PROP &&
+ srv->properties[i].manu_access != MANU_NOT_USER_PROP) {
+ net_buf_simple_add_le16(msg, srv->properties[i].id);
+ }
+ }
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ return;
+ }
+ case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET: {
+ uint16_t property_id = net_buf_simple_pull_le16(buf);
+ send_gen_user_prop_status(model, ctx, property_id, false);
+ return;
+ }
+ default:
+ BT_WARN("Unknown Generic User Property Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+}
+
+static void gen_user_prop_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_user_prop_srv *srv = model->user_data;
+ struct bt_mesh_generic_property *property = NULL;
+ uint16_t property_id = 0U;
+ uint8_t expect_len = 0U;
+
+ if (srv == NULL || srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ property_id = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .user_property_set.id = property_id,
+ .user_property_set.value = buf,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ property = gen_get_user_property(model, property_id);
+ if (property == NULL || property->user_access == USER_ACCESS_PROHIBIT ||
+ property->user_access == USER_ACCESS_READ) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET) {
+ send_gen_user_prop_status(model, ctx, property_id, false);
+ }
+ send_gen_user_prop_status(model, ctx, property_id, true);
+ return;
+ }
+
+ /* For BLE Mesh Model BQB test:
+ * PTS will send Generic User Property Set/Set Unack messages with
+ * invalid device property value length, these messages need to be
+ * ignored, otherwise the test case will fail.
+ */
+ expect_len = bt_mesh_get_dev_prop_len(property_id);
+ if (buf->len != expect_len) {
+ BT_ERR("Invalid User Property 0x%04x length, expect %d, actual %d",
+ property_id, expect_len, buf->len);
+ return;
+ }
+
+ net_buf_simple_reset(property->val);
+ net_buf_simple_add_mem(property->val, buf->data, MIN(buf->len, property->val->size));
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_user_prop_set.id = property_id,
+ .gen_user_prop_set.value = property->val,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET) {
+ send_gen_user_prop_status(model, ctx, property_id, false);
+ }
+ send_gen_user_prop_status(model, ctx, property_id, true);
+}
+
+/* Generic Admin Property Server message handlers */
+static struct bt_mesh_generic_property *gen_get_admin_property(struct bt_mesh_model *model,
+ uint16_t property_id)
+{
+ struct bt_mesh_gen_admin_prop_srv *srv = model->user_data;
+ int i;
+
+ for (i = 0; i < srv->property_count; i++) {
+ if (srv->properties[i].id == property_id) {
+ return &srv->properties[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void send_gen_admin_prop_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t property_id, bool publish)
+{
+ struct bt_mesh_generic_property *property = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t length = 0U;
+
+ if (property_id == BLE_MESH_INVALID_DEVICE_PROPERTY_ID) {
+ BT_ERR("Invalid Admin Property ID 0x%04x", property_id);
+ return;
+ }
+
+ property = gen_get_admin_property(model, property_id);
+ if (property == NULL) {
+ BT_WARN("Admin property 0x%04x not exists", property_id);
+ length = sizeof(property_id);
+ } else {
+ length = sizeof(property->id) + sizeof(property->admin_access) + property->val->len;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, 1 + length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS);
+ if (property == NULL) {
+ net_buf_simple_add_le16(msg, property_id);
+ } else {
+ net_buf_simple_add_le16(msg, property->id);
+ net_buf_simple_add_u8(msg, property->admin_access);
+ if (ctx->recv_op != BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET ||
+ property->admin_access != ADMIN_ACCESS_WRITE) {
+ net_buf_simple_add_mem(msg, property->val->data, property->val->len);
+ }
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_admin_prop_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_admin_prop_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_get_msg_t get = {0};
+ const uint8_t *param = NULL;
+ size_t len = 0U;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET) {
+ get.admin_property_get.id = net_buf_simple_pull_le16(buf);
+ param = (const uint8_t *)&get;
+ len = sizeof(get);
+ }
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, param, len);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET: {
+ struct net_buf_simple *msg = NULL;
+ uint8_t i = 0U;
+ msg = bt_mesh_alloc_buf(1 + srv->property_count * 2 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS);
+ for (i = 0U; i < srv->property_count; i++) {
+ net_buf_simple_add_le16(msg, srv->properties[i].id);
+ }
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ return;
+ }
+ case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET: {
+ uint16_t property_id = net_buf_simple_pull_le16(buf);
+ send_gen_admin_prop_status(model, ctx, property_id, false);
+ return;
+ }
+ default:
+ BT_WARN("Unknown Generic Admin Property Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+}
+
+static void gen_admin_prop_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_admin_prop_srv *srv = model->user_data;
+ struct bt_mesh_generic_property *property = NULL;
+ uint16_t property_id = 0U;
+ uint8_t access = 0U;
+
+ if (srv == NULL || srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ property_id = net_buf_simple_pull_le16(buf);
+ access = net_buf_simple_pull_u8(buf);
+ if (access > ADMIN_ACCESS_READ_WRITE) {
+ BT_ERR("Invalid Admin Access 0x%02x", access);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .admin_property_set.id = property_id,
+ .admin_property_set.access = access,
+ .admin_property_set.value = buf,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ property = gen_get_admin_property(model, property_id);
+ if (property == NULL) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET) {
+ send_gen_admin_prop_status(model, ctx, property_id, false);
+ }
+ send_gen_admin_prop_status(model, ctx, property_id, true);
+ return;
+ }
+
+ property->admin_access = access;
+
+ net_buf_simple_reset(property->val);
+ net_buf_simple_add_mem(property->val, buf->data, MIN(buf->len, property->val->size));
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_admin_prop_set.id = property_id,
+ .gen_admin_prop_set.access = property->admin_access,
+ .gen_admin_prop_set.value = property->val,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET) {
+ send_gen_admin_prop_status(model, ctx, property_id, false);
+ }
+ send_gen_admin_prop_status(model, ctx, property_id, true);
+}
+
+/* Generic Manufacturer Property Server message handlers */
+static struct bt_mesh_generic_property *gen_get_manu_property(struct bt_mesh_model *model,
+ uint16_t property_id)
+{
+ struct bt_mesh_gen_manu_prop_srv *srv = model->user_data;
+ int i;
+
+ for (i = 0; i < srv->property_count; i++) {
+ if (srv->properties[i].id == property_id) {
+ return &srv->properties[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void send_gen_manu_prop_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t property_id, bool publish)
+{
+ struct bt_mesh_generic_property *property = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t length = 0U;
+
+ if (property_id == BLE_MESH_INVALID_DEVICE_PROPERTY_ID) {
+ BT_ERR("Invalid Manu Property ID 0x%04x", property_id);
+ return;
+ }
+
+ property = gen_get_manu_property(model, property_id);
+ if (property == NULL) {
+ BT_WARN("Manu property 0x%04x not exists", property_id);
+ length = sizeof(property_id);
+ } else {
+ length = sizeof(property->id) + sizeof(property->manu_access) + property->val->len;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, 1 + length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS);
+ if (property == NULL) {
+ net_buf_simple_add_le16(msg, property_id);
+ } else {
+ net_buf_simple_add_le16(msg, property->id);
+ net_buf_simple_add_u8(msg, property->manu_access);
+ net_buf_simple_add_mem(msg, property->val->data, property->val->len);
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void gen_manu_prop_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_manu_prop_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_get_msg_t get = {0};
+ const uint8_t *param = NULL;
+ size_t len = 0U;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET) {
+ get.manu_property_get.id = net_buf_simple_pull_le16(buf);
+ param = (const uint8_t *)&get;
+ len = sizeof(get);
+ }
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, param, len);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET: {
+ struct net_buf_simple *msg = NULL;
+ uint8_t i = 0U;
+ msg = bt_mesh_alloc_buf(1 + srv->property_count * 2 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS);
+ for (i = 0U; i < srv->property_count; i++) {
+ net_buf_simple_add_le16(msg, srv->properties[i].id);
+ }
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ return;
+ }
+ case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET: {
+ uint16_t property_id = net_buf_simple_pull_le16(buf);
+ send_gen_manu_prop_status(model, ctx, property_id, false);
+ return;
+ }
+ default:
+ BT_WARN("Unknown Generic Manu Property Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+}
+
+static void gen_manu_prop_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_manu_prop_srv *srv = model->user_data;
+ struct bt_mesh_generic_property *property = NULL;
+ uint16_t property_id = 0U;
+ uint8_t access = 0U;
+
+ if (srv == NULL || srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ property_id = net_buf_simple_pull_le16(buf);
+ access = net_buf_simple_pull_u8(buf);
+ if (access > MANU_ACCESS_READ) {
+ BT_ERR("Invalid Manu Access 0x%02x", access);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_set_msg_t set = {
+ .manu_property_set.id = property_id,
+ .manu_property_set.access = access,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ property = gen_get_manu_property(model, property_id);
+ if (property == NULL) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET) {
+ send_gen_manu_prop_status(model, ctx, property_id, false);
+ }
+ send_gen_manu_prop_status(model, ctx, property_id, true);
+ return;
+ }
+
+ property->manu_access = access;
+
+ bt_mesh_gen_server_state_change_t change = {
+ .gen_manu_prop_set.id = property_id,
+ .gen_manu_prop_set.access = property->manu_access,
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET) {
+ send_gen_manu_prop_status(model, ctx, property_id, false);
+ }
+ send_gen_manu_prop_status(model, ctx, property_id, true);
+}
+
+/* Generic Client Property Server message handlers */
+static int search_prop_id_index(const uint16_t *array, uint8_t array_idx, uint16_t id)
+{
+ static const uint16_t *start = NULL;
+ uint8_t index = 0U;
+ uint16_t temp = 0U;
+
+ if (start == NULL) {
+ start = array;
+ }
+
+ if (array_idx == 0U) {
+ if (*array >= id) {
+ return array - start;
+ }
+
+ return -1;
+ }
+
+ index = array_idx / 2;
+ temp = array[index];
+
+ if (temp == id) {
+ return array + index - start;
+ }
+
+ if (temp > id) {
+ return search_prop_id_index(array, index, id);
+ }
+
+ return search_prop_id_index(array + index + 1, array_idx - 1 - index, id);
+}
+
+static void gen_client_prop_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_gen_client_prop_srv *srv = model->user_data;
+ struct net_buf_simple *sdu = NULL;
+ uint16_t total_len = 5U;
+ uint16_t property_id = 0U;
+ int i, index = 0;
+
+ if (srv == NULL || srv->id_count == 0U || srv->property_ids == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_gen_server_recv_get_msg_t get = {
+ .client_properties_get.id = net_buf_simple_pull_le16(buf),
+ };
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ return;
+ }
+
+ /* The sequence shall be in an ascending order of Property ID values and shall
+ * start with a smallest Property ID that is greater than or equal to the value
+ * of the Generic Client Property field of the Generic Client Properties Get
+ * message that it is responding to.
+ */
+
+ property_id = net_buf_simple_pull_le16(buf);
+ index = search_prop_id_index(srv->property_ids, srv->id_count - 1, property_id);
+ if (index < 0) {
+ NET_BUF_SIMPLE_DEFINE(msg, 1 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ bt_mesh_model_msg_init(&msg, BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS);
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, &msg, NULL, NULL));
+ return;
+ }
+
+ sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN));
+ if (sdu == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+
+ bt_mesh_model_msg_init(sdu, BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS);
+ for (i = index; i < srv->id_count; i++) {
+ total_len += sizeof(uint16_t);
+ if (total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large generic client properties status");
+ break;
+ }
+ net_buf_simple_add_le16(sdu, srv->property_ids[i]);
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, sdu, NULL, NULL));
+
+ bt_mesh_free_buf(sdu);
+}
+
+/* message handlers (End) */
+
+/* Mapping of message handlers for Generic OnOff Server (0x1000) */
+const struct bt_mesh_model_op bt_mesh_gen_onoff_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
+ { BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
+ { BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Level Server (0x1002) */
+const struct bt_mesh_model_op bt_mesh_gen_level_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_LEVEL_GET, 0, gen_level_get },
+ { BLE_MESH_MODEL_OP_GEN_LEVEL_SET, 3, gen_level_set },
+ { BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK, 3, gen_level_set },
+ { BLE_MESH_MODEL_OP_GEN_DELTA_SET, 5, gen_delta_set },
+ { BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK, 5, gen_delta_set },
+ { BLE_MESH_MODEL_OP_GEN_MOVE_SET, 3, gen_move_set },
+ { BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK, 3, gen_move_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Default TT Server (0x1004) */
+const struct bt_mesh_model_op bt_mesh_gen_def_trans_time_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET, 0, gen_def_trans_time_get },
+ { BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET, 1, gen_def_trans_time_set },
+ { BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK, 1, gen_def_trans_time_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power OnOff Server (0x1006) */
+const struct bt_mesh_model_op bt_mesh_gen_power_onoff_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET, 0, gen_onpowerup_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power OnOff Setup Server (0x1007) */
+const struct bt_mesh_model_op bt_mesh_gen_power_onoff_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET, 1, gen_onpowerup_set },
+ { BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK, 1, gen_onpowerup_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power Level Server (0x1009) */
+const struct bt_mesh_model_op bt_mesh_gen_power_level_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET, 0, gen_power_level_get },
+ { BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET, 3, gen_power_level_set },
+ { BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK, 3, gen_power_level_set },
+ { BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET, 0, gen_power_level_get },
+ { BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET, 0, gen_power_level_get },
+ { BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET, 0, gen_power_level_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Power Level Setup Server (0x100A) */
+const struct bt_mesh_model_op bt_mesh_gen_power_level_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET, 2, gen_power_default_set },
+ { BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK, 2, gen_power_default_set },
+ { BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET, 4, gen_power_range_set },
+ { BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK, 4, gen_power_range_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Battery Server (0x100C) */
+const struct bt_mesh_model_op bt_mesh_gen_battery_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_BATTERY_GET, 0, gen_battery_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Location Server (0x100E) */
+const struct bt_mesh_model_op bt_mesh_gen_location_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET, 0, gen_location_get },
+ { BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET, 0, gen_location_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Location Setup Server (0x100F) */
+const struct bt_mesh_model_op bt_mesh_gen_location_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET, 10, gen_location_set },
+ { BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK, 10, gen_location_set },
+ { BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET, 9, gen_location_set },
+ { BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK, 9, gen_location_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic User Property Server (0x1013) */
+const struct bt_mesh_model_op bt_mesh_gen_user_prop_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET, 0, gen_user_prop_get },
+ { BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET, 2, gen_user_prop_get },
+ { BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET, 3, gen_user_prop_set },
+ { BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK, 3, gen_user_prop_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Admin Property Server (0x1011) */
+const struct bt_mesh_model_op bt_mesh_gen_admin_prop_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET, 0, gen_admin_prop_get },
+ { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET, 2, gen_admin_prop_get },
+ { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET, 4, gen_admin_prop_set },
+ { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK, 4, gen_admin_prop_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Manufacturer Property Server (0x1012) */
+const struct bt_mesh_model_op bt_mesh_gen_manu_prop_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET, 0, gen_manu_prop_get },
+ { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET, 2, gen_manu_prop_get },
+ { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET, 3, gen_manu_prop_set },
+ { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET_UNACK, 3, gen_manu_prop_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Generic Client Property Server (0x1014) */
+const struct bt_mesh_model_op bt_mesh_gen_client_prop_srv_op[] = {
+ { BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET, 2, gen_client_prop_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+static inline int property_id_compare(const void *p1, const void *p2)
+{
+ if (*(uint16_t *)p1 < * (uint16_t *)p2) {
+ return -1;
+ }
+
+ if (*(uint16_t *)p1 > *(uint16_t *)p2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int generic_server_init(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Generic Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_GEN_ONOFF_SRV: {
+ struct bt_mesh_gen_onoff_srv *srv = model->user_data;
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, generic_onoff_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_LEVEL_SRV: {
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, generic_level_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV: {
+ struct bt_mesh_gen_def_trans_time_srv *srv = model->user_data;
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV: {
+ struct bt_mesh_gen_power_onoff_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic OnPowerUp State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV: {
+ struct bt_mesh_gen_power_onoff_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic OnPowerUp State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV: {
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power Level State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, generic_power_level_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV: {
+ struct bt_mesh_gen_power_level_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power Level State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_BATTERY_SRV: {
+ struct bt_mesh_gen_battery_srv *srv = model->user_data;
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_LOCATION_SRV: {
+ struct bt_mesh_gen_location_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Location State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_LOCATION_SETUP_SRV: {
+ struct bt_mesh_gen_location_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Location State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_USER_PROP_SRV: {
+ struct bt_mesh_gen_user_prop_srv *srv = model->user_data;
+ if (srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("Invalid Generic User Property State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV: {
+ struct bt_mesh_gen_admin_prop_srv *srv = model->user_data;
+ if (srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("Invalid Generic Admin Property State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV: {
+ struct bt_mesh_gen_manu_prop_srv *srv = model->user_data;
+ if (srv->property_count == 0U || srv->properties == NULL) {
+ BT_ERR("Invalid Generic Manufacturer Property State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV: {
+ struct bt_mesh_gen_client_prop_srv *srv = model->user_data;
+ if (srv->id_count == 0U || srv->property_ids == NULL) {
+ BT_ERR("Invalid Generic Client Property State");
+ return -EINVAL;
+ }
+ /* Quick sort the Client Property IDs in ascending order */
+ qsort(srv->property_ids, srv->id_count, sizeof(uint16_t), property_id_compare);
+ srv->model = model;
+ break;
+ }
+ default:
+ BT_WARN("Unknown Generic Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ bt_mesh_mutex_create(&generic_server_lock);
+
+ return 0;
+}
+
+static int gen_onoff_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic OnOff Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_level_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Level Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_def_trans_time_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Default Trans Time Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_power_onoff_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Power OnOff Server has no publication support");
+ return -EINVAL;
+ }
+
+ /* When this model is present on an element, the corresponding Generic
+ * Power OnOff Setup Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV) == NULL) {
+ BT_WARN("Generic Power OnOff Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return generic_server_init(model);
+}
+
+static int gen_power_onoff_setup_srv_init(struct bt_mesh_model *model)
+{
+ return generic_server_init(model);
+}
+
+static int gen_power_level_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Power Level Server has no publication support");
+ return -EINVAL;
+ }
+
+ /* When this model is present on an Element, the corresponding Generic
+ * Power Level Setup Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV) == NULL) {
+ BT_WARN("Generic Power Level Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return generic_server_init(model);
+}
+
+static int gen_power_level_setup_srv_init(struct bt_mesh_model *model)
+{
+ return generic_server_init(model);
+}
+
+static int gen_battery_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Battery Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_location_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Location Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_location_setup_srv_init(struct bt_mesh_model *model)
+{
+ /* When this model is present on an Element, the corresponding Generic
+ * Location Setup Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_GEN_LOCATION_SETUP_SRV) == NULL) {
+ BT_WARN("Generic Location Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return generic_server_init(model);
+}
+
+static int gen_user_prop_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic User Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_admin_prop_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Admin Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_manu_prop_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Manufacturer Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+static int gen_client_prop_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Client Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_init(model);
+}
+
+#if CONFIG_BLE_MESH_DEINIT
+static int generic_server_deinit(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Generic Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_GEN_ONOFF_SRV: {
+ struct bt_mesh_gen_onoff_srv *srv = model->user_data;
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_LEVEL_SRV: {
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV: {
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power Level State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV:
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV:
+ case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_GEN_BATTERY_SRV:
+ case BLE_MESH_MODEL_ID_GEN_LOCATION_SRV:
+ case BLE_MESH_MODEL_ID_GEN_LOCATION_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_GEN_USER_PROP_SRV:
+ case BLE_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV:
+ case BLE_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV:
+ case BLE_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV:
+ break;
+ default:
+ BT_WARN("Unknown Generic Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ bt_mesh_mutex_free(&generic_server_lock);
+
+ return 0;
+}
+
+static int gen_onoff_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic OnOff Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_level_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Level Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_def_trans_time_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Default Trans Time Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_power_onoff_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Power OnOff Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_power_onoff_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return generic_server_deinit(model);
+}
+
+static int gen_power_level_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Power Level Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_power_level_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return generic_server_deinit(model);
+}
+
+static int gen_battery_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Battery Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_location_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Location Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_location_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return generic_server_deinit(model);
+}
+
+static int gen_user_prop_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic User Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_admin_prop_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Admin Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_manu_prop_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Manufacturer Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+
+static int gen_client_prop_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Generic Client Property Server has no publication support");
+ return -EINVAL;
+ }
+
+ return generic_server_deinit(model);
+}
+#endif /* CONFIG_BLE_MESH_DEINIT */
+
+const struct bt_mesh_model_cb bt_mesh_gen_onoff_srv_cb = {
+ .init = gen_onoff_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_onoff_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_level_srv_cb = {
+ .init = gen_level_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_level_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_def_trans_time_srv_cb = {
+ .init = gen_def_trans_time_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_def_trans_time_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_power_onoff_srv_cb = {
+ .init = gen_power_onoff_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_power_onoff_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_power_onoff_setup_srv_cb = {
+ .init = gen_power_onoff_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_power_onoff_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_power_level_srv_cb = {
+ .init = gen_power_level_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_power_level_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_power_level_setup_srv_cb = {
+ .init = gen_power_level_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_power_level_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_battery_srv_cb = {
+ .init = gen_battery_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_battery_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_location_srv_cb = {
+ .init = gen_location_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_location_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_location_setup_srv_cb = {
+ .init = gen_location_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_location_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_user_prop_srv_cb = {
+ .init = gen_user_prop_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_user_prop_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_admin_prop_srv_cb = {
+ .init = gen_admin_prop_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_admin_prop_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_manu_prop_srv_cb = {
+ .init = gen_manu_prop_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_manu_prop_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_gen_client_prop_srv_cb = {
+ .init = gen_client_prop_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = gen_client_prop_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+#endif /* CONFIG_BLE_MESH_GENERIC_SERVER */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/generic_server.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/generic_server.h
new file mode 100644
index 00000000..2f7ef28f
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/generic_server.h
@@ -0,0 +1,367 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _GENERIC_SERVER_H_
+#define _GENERIC_SERVER_H_
+
+#include "mesh/server_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_mesh_gen_onoff_state {
+ uint8_t onoff;
+ uint8_t target_onoff;
+};
+
+struct bt_mesh_gen_onoff_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_onoff_state state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+};
+
+struct bt_mesh_gen_level_state {
+ int16_t level;
+ int16_t target_level;
+
+ int16_t last_level;
+ int32_t last_delta;
+
+ bool move_start;
+ bool positive;
+};
+
+struct bt_mesh_gen_level_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_level_state state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_level;
+};
+
+struct bt_mesh_gen_def_trans_time_state {
+ uint8_t trans_time;
+};
+
+struct bt_mesh_gen_def_trans_time_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_def_trans_time_state state;
+};
+
+struct bt_mesh_gen_onpowerup_state {
+ uint8_t onpowerup;
+};
+
+struct bt_mesh_gen_power_onoff_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_onpowerup_state *state;
+};
+
+struct bt_mesh_gen_power_onoff_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_onpowerup_state *state;
+};
+
+struct bt_mesh_gen_power_level_state {
+ uint16_t power_actual;
+ uint16_t target_power_actual;
+
+ uint16_t power_last;
+ uint16_t power_default;
+
+ uint8_t status_code;
+ uint16_t power_range_min;
+ uint16_t power_range_max;
+};
+
+struct bt_mesh_gen_power_level_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_power_level_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_level;
+};
+
+struct bt_mesh_gen_power_level_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_power_level_state *state;
+};
+
+struct bt_mesh_gen_battery_state {
+ uint32_t battery_level : 8,
+ time_to_discharge : 24;
+ uint32_t time_to_charge : 24,
+ battery_flags : 8;
+};
+
+struct bt_mesh_gen_battery_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_battery_state state;
+};
+
+struct bt_mesh_gen_location_state {
+ int32_t global_latitude;
+ int32_t global_longitude;
+ int16_t global_altitude;
+ int16_t local_north;
+ int16_t local_east;
+ int16_t local_altitude;
+ uint8_t floor_number;
+ uint16_t uncertainty;
+};
+
+struct bt_mesh_gen_location_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_location_state *state;
+};
+
+struct bt_mesh_gen_location_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_gen_location_state *state;
+};
+
+/**
+ * According to the hierarchy of Generic Property states (Model Spec section 3.1.8),
+ * the Manufacturer Properties and Admin Properties may contain multiple Property
+ * states. User Properties just a collection of which can be accessed.
+ *
+ * property_count: Number of the properties contained in the table
+ * properties: Table of the properties
+ *
+ * These variables need to be initialized in the application layer, the precise
+ * number of the properties should be set and memories used to store the property
+ * values should be allocated.
+ */
+
+enum bt_mesh_gen_user_prop_access {
+ USER_ACCESS_PROHIBIT,
+ USER_ACCESS_READ,
+ USER_ACCESS_WRITE,
+ USER_ACCESS_READ_WRITE,
+};
+
+enum bt_mesh_gen_admin_prop_access {
+ ADMIN_NOT_USER_PROP,
+ ADMIN_ACCESS_READ,
+ ADMIN_ACCESS_WRITE,
+ ADMIN_ACCESS_READ_WRITE,
+};
+
+enum bt_mesh_gen_manu_prop_access {
+ MANU_NOT_USER_PROP,
+ MANU_ACCESS_READ,
+};
+
+struct bt_mesh_generic_property {
+ uint16_t id;
+ uint8_t user_access;
+ uint8_t admin_access;
+ uint8_t manu_access;
+ struct net_buf_simple *val;
+};
+
+struct bt_mesh_gen_user_prop_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ uint8_t property_count;
+ struct bt_mesh_generic_property *properties;
+};
+
+struct bt_mesh_gen_admin_prop_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ uint8_t property_count;
+ struct bt_mesh_generic_property *properties;
+};
+
+struct bt_mesh_gen_manu_prop_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ uint8_t property_count;
+ struct bt_mesh_generic_property *properties;
+};
+
+struct bt_mesh_gen_client_prop_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ uint8_t id_count;
+ uint16_t *property_ids;
+};
+
+typedef union {
+ struct {
+ uint8_t onoff;
+ } gen_onoff_set;
+ struct {
+ int16_t level;
+ } gen_level_set;
+ struct {
+ int16_t level;
+ } gen_delta_set;
+ struct {
+ int16_t level;
+ } gen_move_set;
+ struct {
+ uint8_t trans_time;
+ } gen_def_trans_time_set;
+ struct {
+ uint8_t onpowerup;
+ } gen_onpowerup_set;
+ struct {
+ uint16_t power;
+ } gen_power_level_set;
+ struct {
+ uint16_t power;
+ } gen_power_default_set;
+ struct {
+ uint16_t range_min;
+ uint16_t range_max;
+ } gen_power_range_set;
+ struct {
+ int32_t latitude;
+ int32_t longitude;
+ int16_t altitude;
+ } gen_loc_global_set;
+ struct {
+ int16_t north;
+ int16_t east;
+ int16_t altitude;
+ uint8_t floor_number;
+ uint16_t uncertainty;
+ } gen_loc_local_set;
+ struct {
+ uint16_t id;
+ struct net_buf_simple *value;
+ } gen_user_prop_set;
+ struct {
+ uint16_t id;
+ uint8_t access;
+ struct net_buf_simple *value;
+ } gen_admin_prop_set;
+ struct {
+ uint16_t id;
+ uint8_t access;
+ } gen_manu_prop_set;
+} bt_mesh_gen_server_state_change_t;
+
+typedef union {
+ struct {
+ uint16_t id;
+ } user_property_get;
+ struct {
+ uint16_t id;
+ } admin_property_get;
+ struct {
+ uint16_t id;
+ } manu_property_get;
+ struct {
+ uint16_t id;
+ } client_properties_get;
+} bt_mesh_gen_server_recv_get_msg_t;
+
+typedef union {
+ struct {
+ bool op_en;
+ uint8_t onoff;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } onoff_set;
+ struct {
+ bool op_en;
+ int16_t level;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } level_set;
+ struct {
+ bool op_en;
+ int32_t delta_level;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } delta_set;
+ struct {
+ bool op_en;
+ int16_t delta_level;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } move_set;
+ struct {
+ uint8_t trans_time;
+ } def_trans_time_set;
+ struct {
+ uint8_t onpowerup;
+ } onpowerup_set;
+ struct {
+ bool op_en;
+ uint16_t power;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } power_level_set;
+ struct {
+ uint16_t power;
+ } power_default_set;
+ struct {
+ uint16_t range_min;
+ uint16_t range_max;
+ } power_range_set;
+ struct {
+ int32_t latitude;
+ int32_t longitude;
+ int16_t altitude;
+ } loc_global_set;
+ struct {
+ int16_t north;
+ int16_t east;
+ int16_t altitude;
+ uint8_t floor_number;
+ uint16_t uncertainty;
+ } loc_local_set;
+ struct {
+ uint16_t id;
+ struct net_buf_simple *value;
+ } user_property_set;
+ struct {
+ uint16_t id;
+ uint8_t access;
+ struct net_buf_simple *value;
+ } admin_property_set;
+ struct {
+ uint16_t id;
+ uint8_t access;
+ } manu_property_set;
+} bt_mesh_gen_server_recv_set_msg_t;
+
+void bt_mesh_generic_server_lock(void);
+void bt_mesh_generic_server_unlock(void);
+
+void gen_onoff_publish(struct bt_mesh_model *model);
+void gen_level_publish(struct bt_mesh_model *model);
+void gen_onpowerup_publish(struct bt_mesh_model *model);
+void gen_power_level_publish(struct bt_mesh_model *model, uint16_t opcode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GENERIC_SERVER_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/lighting_server.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/lighting_server.h
new file mode 100644
index 00000000..d6ce18ff
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/lighting_server.h
@@ -0,0 +1,510 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _LIGHTING_SERVER_H_
+#define _LIGHTING_SERVER_H_
+
+#include "mesh/server_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_mesh_light_lightness_state {
+ uint16_t lightness_linear;
+ uint16_t target_lightness_linear;
+
+ uint16_t lightness_actual;
+ uint16_t target_lightness_actual;
+
+ uint16_t lightness_last;
+ uint16_t lightness_default;
+
+ uint8_t status_code;
+ uint16_t lightness_range_min;
+ uint16_t lightness_range_max;
+};
+
+struct bt_mesh_light_lightness_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_lightness_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition actual_transition;
+ struct bt_mesh_state_transition linear_transition;
+ int32_t tt_delta_lightness_actual;
+ int32_t tt_delta_lightness_linear;
+};
+
+struct bt_mesh_light_lightness_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_lightness_state *state;
+};
+
+struct bt_mesh_light_ctl_state {
+ uint16_t lightness;
+ uint16_t target_lightness;
+
+ uint16_t temperature;
+ uint16_t target_temperature;
+
+ int16_t delta_uv;
+ int16_t target_delta_uv;
+
+ uint8_t status_code;
+ uint16_t temperature_range_min;
+ uint16_t temperature_range_max;
+
+ uint16_t lightness_default;
+ uint16_t temperature_default;
+ int16_t delta_uv_default;
+};
+
+struct bt_mesh_light_ctl_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_ctl_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_lightness;
+ int32_t tt_delta_temperature;
+ int32_t tt_delta_delta_uv;
+};
+
+struct bt_mesh_light_ctl_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_ctl_state *state;
+};
+
+struct bt_mesh_light_ctl_temp_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_ctl_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_temperature;
+ int32_t tt_delta_delta_uv;
+};
+
+struct bt_mesh_light_hsl_state {
+ uint16_t lightness;
+ uint16_t target_lightness;
+
+ uint16_t hue;
+ uint16_t target_hue;
+
+ uint16_t saturation;
+ uint16_t target_saturation;
+
+ uint16_t lightness_default;
+ uint16_t hue_default;
+ uint16_t saturation_default;
+
+ uint8_t status_code;
+ uint16_t hue_range_min;
+ uint16_t hue_range_max;
+ uint16_t saturation_range_min;
+ uint16_t saturation_range_max;
+};
+
+struct bt_mesh_light_hsl_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_hsl_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_lightness;
+ int32_t tt_delta_hue;
+ int32_t tt_delta_saturation;
+};
+
+struct bt_mesh_light_hsl_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_hsl_state *state;
+};
+
+struct bt_mesh_light_hsl_hue_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_hsl_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_hue;
+};
+
+struct bt_mesh_light_hsl_sat_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_hsl_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_saturation;
+};
+
+struct bt_mesh_light_xyl_state {
+ uint16_t lightness;
+ uint16_t target_lightness;
+
+ uint16_t x;
+ uint16_t target_x;
+
+ uint16_t y;
+ uint16_t target_y;
+
+ uint16_t lightness_default;
+ uint16_t x_default;
+ uint16_t y_default;
+
+ uint8_t status_code;
+ uint16_t x_range_min;
+ uint16_t x_range_max;
+ uint16_t y_range_min;
+ uint16_t y_range_max;
+};
+
+struct bt_mesh_light_xyl_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_xyl_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+ int32_t tt_delta_lightness;
+ int32_t tt_delta_x;
+ int32_t tt_delta_y;
+};
+
+struct bt_mesh_light_xyl_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_xyl_state *state;
+};
+
+struct bt_mesh_light_lc_state {
+ uint32_t mode : 1, /* default 0 */
+ occupancy_mode : 1, /* default 1 */
+ light_onoff : 1,
+ target_light_onoff : 1,
+ occupancy : 1,
+ ambient_luxlevel : 24; /* 0x000000 ~ 0xFFFFFF */
+
+ uint16_t linear_output; /* 0x0000 ~ 0xFFFF */
+};
+
+struct bt_mesh_light_lc_property_state {
+ uint32_t time_occupancy_delay; /* 0x003A */
+ uint32_t time_fade_on; /* 0x0037 */
+ uint32_t time_run_on; /* 0x003C */
+ uint32_t time_fade; /* 0x0036 */
+ uint32_t time_prolong; /* 0x003B */
+ uint32_t time_fade_standby_auto; /* 0x0038 */
+ uint32_t time_fade_standby_manual; /* 0x0039 */
+
+ uint16_t lightness_on; /* 0x002E */
+ uint16_t lightness_prolong; /* 0x002F */
+ uint16_t lightness_standby; /* 0x0030 */
+
+ uint16_t ambient_luxlevel_on; /* 0x002B, 0x0000 ~ 0xFFFF */
+ uint16_t ambient_luxlevel_prolong; /* 0x002C, 0x0000 ~ 0xFFFF */
+ uint16_t ambient_luxlevel_standby; /* 0x002D, 0x0000 ~ 0xFFFF */
+
+ float regulator_kiu; /* 0x0033, 0.0 ~ 1000.0, default 250.0 */
+ float regulator_kid; /* 0x0032, 0.0 ~ 1000.0, default 25.0 */
+ float regulator_kpu; /* 0x0035, 0.0 ~ 1000.0, default 80.0 */
+ float regulator_kpd; /* 0x0034, 0.0 ~ 1000.0, default 80.0 */
+ int8_t regulator_accuracy; /* 0x0031, 0.0 ~ 100.0, default 2.0 */
+
+ uint32_t set_occupancy_to_1_delay;
+};
+
+typedef enum {
+ LC_OFF,
+ LC_STANDBY,
+ LC_FADE_ON,
+ LC_RUN,
+ LC_FADE,
+ LC_PROLONG,
+ LC_FADE_STANDBY_AUTO,
+ LC_FADE_STANDBY_MANUAL,
+} bt_mesh_lc_state;
+
+struct bt_mesh_light_lc_state_machine {
+ struct {
+ uint8_t fade_on;
+ uint8_t fade;
+ uint8_t fade_standby_auto;
+ uint8_t fade_standby_manual;
+ } trans_time;
+ bt_mesh_lc_state state;
+ struct k_delayed_work timer;
+};
+
+struct bt_mesh_light_control {
+ struct bt_mesh_light_lc_state state;
+ struct bt_mesh_light_lc_property_state prop_state;
+ struct bt_mesh_light_lc_state_machine state_machine;
+};
+
+struct bt_mesh_light_lc_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_control *lc;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+};
+
+struct bt_mesh_light_lc_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_light_control *lc;
+};
+
+typedef union {
+ struct {
+ uint16_t lightness;
+ } lightness_set;
+ struct {
+ uint16_t lightness;
+ } lightness_linear_set;
+ struct {
+ uint16_t lightness;
+ } lightness_default_set;
+ struct {
+ uint16_t range_min;
+ uint16_t range_max;
+ } lightness_range_set;
+ struct {
+ uint16_t lightness;
+ uint16_t temperature;
+ int16_t delta_uv;
+ } ctl_set;
+ struct {
+ uint16_t temperature;
+ int16_t delta_uv;
+ } ctl_temp_set;
+ struct {
+ uint16_t range_min;
+ uint16_t range_max;
+ } ctl_temp_range_set;
+ struct {
+ uint16_t lightness;
+ uint16_t temperature;
+ int16_t delta_uv;
+ } ctl_default_set;
+ struct {
+ uint16_t lightness;
+ uint16_t hue;
+ uint16_t saturation;
+ } hsl_set;
+ struct {
+ uint16_t hue;
+ } hsl_hue_set;
+ struct {
+ uint16_t saturation;
+ } hsl_saturation_set;
+ struct {
+ uint16_t lightness;
+ uint16_t hue;
+ uint16_t saturation;
+ } hsl_default_set;
+ struct {
+ uint16_t hue_range_min;
+ uint16_t hue_range_max;
+ uint16_t sat_range_min;
+ uint16_t sat_range_max;
+ } hsl_range_set;
+ struct {
+ uint16_t lightness;
+ uint16_t x;
+ uint16_t y;
+ } xyl_set;
+ struct {
+ uint16_t lightness;
+ uint16_t x;
+ uint16_t y;
+ } xyl_default_set;
+ struct {
+ uint16_t x_range_min;
+ uint16_t x_range_max;
+ uint16_t y_range_min;
+ uint16_t y_range_max;
+ } xyl_range_set;
+ struct {
+ uint8_t mode;
+ } lc_mode_set;
+ struct {
+ uint8_t mode;
+ } lc_om_set;
+ struct {
+ uint8_t onoff;
+ } lc_light_onoff_set;
+ struct {
+ uint16_t id;
+ struct net_buf_simple *value;
+ } lc_property_set;
+ struct {
+ uint16_t property_id;
+ union {
+ uint8_t occupancy;
+ uint32_t set_occupancy_to_1_delay;
+ uint32_t ambient_luxlevel;
+ } state;
+ } sensor_status;
+} bt_mesh_light_server_state_change_t;
+
+typedef union {
+ struct {
+ uint16_t id;
+ } lc_property_get;
+} bt_mesh_light_server_recv_get_msg_t;
+
+typedef union {
+ struct {
+ bool op_en;
+ uint16_t lightness;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } lightness_set;
+ struct {
+ bool op_en;
+ uint16_t lightness;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } lightness_linear_set;
+ struct {
+ uint16_t lightness;
+ } lightness_default_set;
+ struct {
+ uint16_t range_min;
+ uint16_t range_max;
+ } lightness_range_set;
+ struct {
+ bool op_en;
+ uint16_t lightness;
+ uint16_t temperature;
+ int16_t delta_uv;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } ctl_set;
+ struct {
+ bool op_en;
+ uint16_t temperature;
+ int16_t delta_uv;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } ctl_temp_set;
+ struct {
+ uint16_t range_min;
+ uint16_t range_max;
+ } ctl_temp_range_set;
+ struct {
+ uint16_t lightness;
+ uint16_t temperature;
+ int16_t delta_uv;
+ } ctl_default_set;
+ struct {
+ bool op_en;
+ uint16_t lightness;
+ uint16_t hue;
+ uint16_t saturation;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } hsl_set;
+ struct {
+ bool op_en;
+ uint16_t hue;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } hsl_hue_set;
+ struct {
+ bool op_en;
+ uint16_t saturation;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } hsl_saturation_set;
+ struct {
+ uint16_t lightness;
+ uint16_t hue;
+ uint16_t saturation;
+ } hsl_default_set;
+ struct {
+ uint16_t hue_range_min;
+ uint16_t hue_range_max;
+ uint16_t sat_range_min;
+ uint16_t sat_range_max;
+ } hsl_range_set;
+ struct {
+ bool op_en;
+ uint16_t lightness;
+ uint16_t x;
+ uint16_t y;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } xyl_set;
+ struct {
+ uint16_t lightness;
+ uint16_t x;
+ uint16_t y;
+ } xyl_default_set;
+ struct {
+ uint16_t x_range_min;
+ uint16_t x_range_max;
+ uint16_t y_range_min;
+ uint16_t y_range_max;
+ } xyl_range_set;
+ struct {
+ uint8_t mode;
+ } lc_mode_set;
+ struct {
+ uint8_t mode;
+ } lc_om_set;
+ struct {
+ bool op_en;
+ uint8_t light_onoff;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } lc_light_onoff_set;
+ struct {
+ uint16_t id;
+ struct net_buf_simple *value;
+ } lc_property_set;
+} bt_mesh_light_server_recv_set_msg_t;
+
+typedef union {
+ struct {
+ struct net_buf_simple *data;
+ } sensor_status;
+} bt_mesh_light_server_recv_status_msg_t;
+
+void bt_mesh_light_server_lock(void);
+void bt_mesh_light_server_unlock(void);
+
+uint8_t *bt_mesh_get_lc_prop_value(struct bt_mesh_model *model, uint16_t prop_id);
+
+void light_lightness_publish(struct bt_mesh_model *model, uint16_t opcode);
+void light_ctl_publish(struct bt_mesh_model *model, uint16_t opcode);
+void light_hsl_publish(struct bt_mesh_model *model, uint16_t opcode);
+void light_xyl_publish(struct bt_mesh_model *model, uint16_t opcode);
+void light_lc_publish(struct bt_mesh_model *model, uint16_t opcode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIGHTING_SERVER_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/sensor_server.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/sensor_server.h
new file mode 100644
index 00000000..a9fe77ef
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/sensor_server.h
@@ -0,0 +1,246 @@
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _SENSOR_SERVER_H_
+#define _SENSOR_SERVER_H_
+
+#include "mesh/server_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Sensor Property ID related */
+#define INVALID_SENSOR_PROPERTY_ID 0x0000
+
+#define SENSOR_PROPERTY_ID_LEN 0x02
+
+/* Sensor Descriptor state related */
+#define SENSOR_DESCRIPTOR_LEN 0x08
+
+#define SENSOR_UNSPECIFIED_POS_TOLERANCE 0x000
+#define SENSOR_UNSPECIFIED_NEG_TOLERANCE 0x000
+
+#define SENSOR_NOT_APPL_MEASURE_PERIOD 0x00
+
+#define SENSOR_NOT_APPL_UPDATE_INTERVAL 0x00
+
+/* Sensor Setting state related */
+#define INVALID_SENSOR_SETTING_PROPERTY_ID 0x0000
+
+#define SENSOR_SETTING_PROPERTY_ID_LEN 0x02
+#define SENSOR_SETTING_ACCESS_LEN 0x01
+
+#define SENSOR_SETTING_ACCESS_READ 0x01
+#define SENSOR_SETTING_ACCESS_READ_WRITE 0x03
+
+/* Sensor Cadence state related */
+#define SENSOR_DIVISOR_TRIGGER_TYPE_LEN 0x01
+#define SENSOR_STATUS_MIN_INTERVAL_LEN 0x01
+
+#define SENSOR_PERIOD_DIVISOR_MAX_VALUE 15
+
+#define SENSOR_STATUS_MIN_INTERVAL_MAX 26
+
+#define SENSOR_STATUS_TRIGGER_TYPE_CHAR 0
+#define SENSOR_STATUS_TRIGGER_TYPE_UINT16 1
+
+#define SENSOR_STATUS_TRIGGER_UINT16_LEN 0x02
+
+/* Sensor Data state related */
+#define SENSOR_DATA_FORMAT_A 0x00
+#define SENSOR_DATA_FORMAT_B 0x01
+
+#define SENSOR_DATA_FORMAT_A_MPID_LEN 0x02
+#define SENSOR_DATA_FORMAT_B_MPID_LEN 0x03
+
+#define SENSOR_DATA_ZERO_LEN 0x7F
+
+enum bt_mesh_sensor_sample_func {
+ UNSPECIFIED,
+ INSTANTANEOUS,
+ ARITHMETIC_MEAN,
+ RMS,
+ MAXIMUM,
+ MINIMUM,
+ ACCUMULATED,
+ COUNT,
+};
+
+struct sensor_descriptor {
+ uint32_t positive_tolerance : 12,
+ negative_tolerance : 12,
+ sample_function : 8;
+ uint8_t measure_period;
+ uint8_t update_interval;
+};
+
+struct sensor_setting {
+ uint16_t property_id;
+ uint8_t access;
+ /* Or use union to include all possible types */
+ struct net_buf_simple *raw;
+};
+
+struct sensor_cadence {
+ uint8_t period_divisor : 7,
+ trigger_type : 1;
+ struct net_buf_simple *trigger_delta_down;
+ struct net_buf_simple *trigger_delta_up;
+ uint8_t min_interval;
+ struct net_buf_simple *fast_cadence_low;
+ struct net_buf_simple *fast_cadence_high;
+};
+
+struct sensor_data {
+ /**
+ * Format A: The Length field is a 1-based uint4 value (valid range 0x0–0xF,
+ * representing range of 1 – 16).
+ * Format B: The Length field is a 1-based uint7 value (valid range 0x0–0x7F,
+ * representing range of 1 – 127). The value 0x7F represents a
+ * length of zero.
+ */
+ uint8_t format : 1,
+ length : 7;
+ struct net_buf_simple *raw_value;
+};
+
+struct sensor_series_column {
+ struct net_buf_simple *raw_value_x;
+ struct net_buf_simple *column_width;
+ struct net_buf_simple *raw_value_y;
+};
+
+struct bt_mesh_sensor_state {
+ uint16_t sensor_property_id;
+
+ /* Constant throughout the lifetime of an element */
+ struct sensor_descriptor descriptor;
+
+ /* Multiple Sensor Setting states may be present for each sensor.
+ * The Sensor Setting Property ID values shall be unique for each
+ * Sensor Property ID that identifies a sensor within an element.
+ */
+ const uint8_t setting_count;
+ struct sensor_setting *settings;
+
+ /* The Sensor Cadence state may be not supported by sensors based
+ * on device properties referencing "non-scalar characteristics"
+ * such as "histograms" or "composite characteristics".
+ */
+ struct sensor_cadence *cadence;
+
+ struct sensor_data sensor_data;
+
+ /* Values measured by sensors may be organized as arrays (and
+ * represented as series of columns, such as histograms).
+ * 1. The Sensor Raw Value X field has a size and representation
+ * defined by the Sensor Property ID and represents the left
+ * corner of the column on the X axis.
+ * 2. The Sensor Column Width field has a size and representation
+ * defined by the Sensor Property ID and represents the width
+ * of the column on the X axis.
+ * 3. The Sensor Raw Value Y field has a size and representation
+ * defined by the Sensor Property ID and represents the height
+ * of the column on the Y axis.
+ * Note: Values outside the bins defined by a Sensor Property are
+ * not included. For example, if the histogram is defined as 3 bins
+ * representing “lamp operating hours in a given temperature range”
+ * and the bins are [40,60), [60, 80), and [80,100], then any hours
+ * outside that [40, 100] range would not be included.
+ */
+ struct sensor_series_column series_column;
+};
+
+/* 1. Multiple instances of the Sensor states may be present within the
+ * same model, provided that each instance has a unique value of the
+ * Sensor Property ID to allow the instances to be differentiated.
+ * 2. Note: The number of sensors within a multi-sensor is limited by the
+ * size of the message payload for the Sensor Descriptor Status message.
+ * A single Sensor Descriptor may be sent using a single Unsegmented
+ * Access message. Using Segmentation and Reassembly (SAR), up to 38
+ * Sensor Descriptor states may be sent.
+ */
+
+struct bt_mesh_sensor_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ const uint8_t state_count;
+ struct bt_mesh_sensor_state *states;
+};
+
+struct bt_mesh_sensor_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ const uint8_t state_count;
+ struct bt_mesh_sensor_state *states;
+};
+
+typedef union {
+ struct {
+ uint16_t id;
+ uint8_t period_divisor : 7,
+ trigger_type : 1;
+ struct net_buf_simple *trigger_delta_down;
+ struct net_buf_simple *trigger_delta_up;
+ uint8_t min_interval;
+ struct net_buf_simple *fast_cadence_low;
+ struct net_buf_simple *fast_cadence_high;
+ } sensor_cadence_set;
+ struct {
+ uint16_t id;
+ uint16_t setting_id;
+ struct net_buf_simple *value;
+ } sensor_setting_set;
+} bt_mesh_sensor_server_state_change_t;
+
+typedef union {
+ struct {
+ bool op_en;
+ uint16_t id;
+ } sensor_descriptor_get;
+ struct {
+ uint16_t id;
+ } sensor_cadence_get;
+ struct {
+ uint16_t id;
+ } sensor_settings_get;
+ struct {
+ uint16_t id;
+ uint16_t setting_id;
+ } sensor_setting_get;
+ struct {
+ bool op_en;
+ uint16_t id;
+ } sensor_get;
+ struct {
+ uint16_t id;
+ struct net_buf_simple *raw_x;
+ } sensor_column_get;
+ struct {
+ bool op_en;
+ uint16_t id;
+ struct net_buf_simple *raw;
+ } sensor_series_get;
+} bt_mesh_sensor_server_recv_get_msg_t;
+
+typedef union {
+ struct {
+ uint16_t id;
+ struct net_buf_simple *cadence;
+ } sensor_cadence_set;
+ struct {
+ uint16_t id;
+ uint16_t setting_id;
+ struct net_buf_simple *raw;
+ } sensor_setting_set;
+} bt_mesh_sensor_server_recv_set_msg_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SENSOR_SERVER_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/server_common.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/server_common.h
new file mode 100644
index 00000000..b8bf52f2
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/server_common.h
@@ -0,0 +1,124 @@
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _SERVER_COMMON_H_
+#define _SERVER_COMMON_H_
+
+#include <string.h>
+#include <stdint.h>
+#include "mesh/access.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLE_MESH_SERVER_RSP_MAX_LEN 384
+
+#define BLE_MESH_SERVER_TRANS_MIC_SIZE 4
+
+#define BLE_MESH_CHECK_SEND_STATUS(_func) do { \
+ int __status = (_func); \
+ if (__status) { \
+ BT_ERR("%s, Send failed, err %d", __func__, __status); \
+ } \
+ } while(0);
+
+#define BLE_MESH_STATE_OFF 0x00
+#define BLE_MESH_STATE_ON 0x01
+#define BLE_MESH_STATE_RESTORE 0x02
+
+/* Following 4 values are as per Mesh Model specification */
+#define BLE_MESH_LIGHTNESS_MIN 0x0001
+#define BLE_MESH_LIGHTNESS_MAX 0xFFFF
+#define BLE_MESH_TEMPERATURE_MIN 0x0320
+#define BLE_MESH_TEMPERATURE_MAX 0x4E20
+#define BLE_MESH_TEMPERATURE_UNKNOWN 0xFFFF
+
+/* Refer 7.2 of Mesh Model Specification */
+#define BLE_MESH_RANGE_UPDATE_SUCCESS 0x00
+#define BLE_MESH_CANNOT_SET_RANGE_MIN 0x01
+#define BLE_MESH_CANNOT_SET_RANGE_MAX 0x02
+
+#define BLE_MESH_UNKNOWN_REMAIN_TIME 0x3F
+#define BLE_MESH_DEVICE_SPECIFIC_RESOLUTION 10
+
+enum {
+ BLE_MESH_TRANS_TIMER_START, /* Proper transition timer has been started */
+ BLE_MESH_TRANS_FLAG_MAX,
+};
+
+struct bt_mesh_state_transition {
+ bool just_started;
+
+ uint8_t trans_time;
+ uint8_t remain_time;
+ uint8_t delay;
+ uint32_t quo_tt;
+ uint32_t counter;
+ uint32_t total_duration;
+ int64_t start_timestamp;
+
+ BLE_MESH_ATOMIC_DEFINE(flag, BLE_MESH_TRANS_FLAG_MAX);
+ struct k_delayed_work timer;
+};
+
+struct bt_mesh_last_msg_info {
+ uint8_t tid;
+ uint16_t src;
+ uint16_t dst;
+ int64_t timestamp;
+};
+
+#define BLE_MESH_SERVER_RSP_BY_APP 0
+#define BLE_MESH_SERVER_AUTO_RSP 1
+
+struct bt_mesh_server_rsp_ctrl {
+ /**
+ * @brief BLE Mesh Server Response Option
+ * 1. If get_auto_rsp is set to BLE_MESH_SERVER_RSP_BY_APP, then the response
+ * of Client Get messages need to be replied by the application;
+ * 2. If get_auto_rsp is set to BLE_MESH_SERVER_AUTO_RSP, then the response
+ * of Client Get messages will be replied by the server models;
+ * 3. If set_auto_rsp is set to BLE_MESH_SERVER_RSP_BY_APP, then the response
+ * of Client Set messages need to be replied by the application;
+ * 4. If set_auto_rsp is set to BLE_MESH_SERVER_AUTO_RSP, then the response
+ * of Client Set messages will be replied by the server models;
+ * 5. If status_auto_rsp is set to BLE_MESH_SERVER_RSP_BY_APP, then the response
+ * of Server Status messages need to be replied by the application;
+ * 6. If status_auto_rsp is set to BLE_MESH_SERVER_AUTO_RSP, then the response
+ * of Server status messages will be replied by the server models;
+ */
+ uint8_t get_auto_rsp : 1, /* Response for Client Get messages */
+ set_auto_rsp : 1, /* Response for Client Set messages */
+ status_auto_rsp : 1; /* Response for Server Status messages */
+};
+
+uint8_t bt_mesh_get_default_trans_time(struct bt_mesh_model *model);
+
+int bt_mesh_get_light_lc_trans_time(struct bt_mesh_model *model, uint8_t *trans_time);
+
+int bt_mesh_server_get_optional(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf,
+ uint8_t *trans_time, uint8_t *delay,
+ bool *optional);
+
+void bt_mesh_server_alloc_ctx(struct k_work *work);
+void bt_mesh_server_free_ctx(struct k_work *work);
+
+bool bt_mesh_is_server_recv_last_msg(struct bt_mesh_last_msg_info *last,
+ uint8_t tid, uint16_t src, uint16_t dst, int64_t *now);
+
+void bt_mesh_server_update_last_msg(struct bt_mesh_last_msg_info *last,
+ uint8_t tid, uint16_t src, uint16_t dst, int64_t *now);
+
+struct net_buf_simple *bt_mesh_server_get_pub_msg(struct bt_mesh_model *model, uint16_t msg_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SERVER_COMMON_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/state_binding.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/state_binding.h
new file mode 100644
index 00000000..92b77756
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/state_binding.h
@@ -0,0 +1,108 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _STATE_BINDING_H_
+#define _STATE_BINDING_H_
+
+#include "mesh/access.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ GENERIC_ONOFF_STATE,
+ GENERIC_LEVEL_STATE,
+ GENERIC_ONPOWERUP_STATE,
+ GENERIC_POWER_ACTUAL_STATE,
+ LIGHT_LIGHTNESS_ACTUAL_STATE,
+ LIGHT_LIGHTNESS_LINEAR_STATE,
+ LIGHT_CTL_LIGHTNESS_STATE,
+ LIGHT_CTL_TEMP_DELTA_UV_STATE,
+ LIGHT_HSL_STATE,
+ LIGHT_HSL_LIGHTNESS_STATE,
+ LIGHT_HSL_HUE_STATE,
+ LIGHT_HSL_SATURATION_STATE,
+ LIGHT_XYL_LIGHTNESS_STATE,
+ LIGHT_LC_LIGHT_ONOFF_STATE,
+ BIND_STATE_MAX,
+} bt_mesh_server_state_type_t;
+
+typedef union {
+ struct {
+ uint8_t onoff;
+ } gen_onoff;
+ struct {
+ int16_t level;
+ } gen_level;
+ struct {
+ uint8_t onpowerup;
+ } gen_onpowerup;
+ struct {
+ uint16_t power;
+ } gen_power_actual;
+ struct {
+ uint16_t lightness;
+ } light_lightness_actual;
+ struct {
+ uint16_t lightness;
+ } light_lightness_linear;
+ struct {
+ uint16_t lightness;
+ } light_ctl_lightness;
+ struct {
+ uint16_t temperature;
+ int16_t delta_uv;
+ } light_ctl_temp_delta_uv;
+ struct {
+ uint16_t lightness;
+ uint16_t hue;
+ uint16_t saturation;
+ } light_hsl;
+ struct {
+ uint16_t lightness;
+ } light_hsl_lightness;
+ struct {
+ uint16_t hue;
+ } light_hsl_hue;
+ struct {
+ uint16_t saturation;
+ } light_hsl_saturation;
+ struct {
+ uint16_t lightness;
+ } light_xyl_lightness;
+ struct {
+ uint8_t onoff;
+ } light_lc_light_onoff;
+} bt_mesh_server_state_value_t;
+
+uint16_t bt_mesh_convert_lightness_actual_to_linear(uint16_t actual);
+
+uint16_t bt_mesh_convert_lightness_linear_to_actual(uint16_t linear);
+
+int16_t bt_mesh_convert_temperature_to_gen_level(uint16_t temp, uint16_t min, uint16_t max);
+
+uint16_t bt_mesh_covert_gen_level_to_temperature(int16_t level, uint16_t min, uint16_t max);
+
+int16_t bt_mesh_convert_hue_to_level(uint16_t hue);
+
+uint16_t bt_mesh_convert_level_to_hue(int16_t level);
+
+int16_t bt_mesh_convert_saturation_to_level(uint16_t saturation);
+
+uint16_t bt_mesh_convert_level_to_saturation(int16_t level);
+
+int bt_mesh_update_binding_state(struct bt_mesh_model *model,
+ bt_mesh_server_state_type_t type,
+ bt_mesh_server_state_value_t *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STATE_BINDING_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/state_transition.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/state_transition.h
new file mode 100644
index 00000000..ae0bdcda
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/state_transition.h
@@ -0,0 +1,100 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _STATE_TRANSITION_H_
+#define _STATE_TRANSITION_H_
+
+#include "mesh/server_common.h"
+#include "mesh/generic_server.h"
+#include "mesh/sensor_server.h"
+#include "mesh/lighting_server.h"
+#include "mesh/time_scene_server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void bt_mesh_server_calc_remain_time(struct bt_mesh_state_transition *transition);
+
+/* APIs used to get server model transition time values */
+
+void generic_onoff_tt_values(struct bt_mesh_gen_onoff_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void generic_level_tt_values(struct bt_mesh_gen_level_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void generic_power_level_tt_values(struct bt_mesh_gen_power_level_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_lightness_actual_tt_values(struct bt_mesh_light_lightness_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_lightness_linear_tt_values(struct bt_mesh_light_lightness_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_ctl_tt_values(struct bt_mesh_light_ctl_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_ctl_temp_tt_values(struct bt_mesh_light_ctl_temp_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_hsl_tt_values(struct bt_mesh_light_hsl_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_hsl_hue_tt_values(struct bt_mesh_light_hsl_hue_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_hsl_sat_tt_values(struct bt_mesh_light_hsl_sat_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_xyl_tt_values(struct bt_mesh_light_xyl_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void light_lc_tt_values(struct bt_mesh_light_lc_srv *srv,
+ uint8_t trans_time, uint8_t delay);
+
+void scene_tt_values(struct bt_mesh_scene_srv *srv, uint8_t trans_time, uint8_t delay);
+
+/* Server model transition timer handlers */
+
+void generic_onoff_work_handler(struct k_work *work);
+
+void generic_level_work_handler(struct k_work *work);
+
+void generic_power_level_work_handler(struct k_work *work);
+
+void light_lightness_actual_work_handler(struct k_work *work);
+
+void light_lightness_linear_work_handler(struct k_work *work);
+
+void light_ctl_work_handler(struct k_work *work);
+
+void light_ctl_temp_work_handler(struct k_work *work);
+
+void light_hsl_work_handler(struct k_work *work);
+
+void light_hsl_hue_work_handler(struct k_work *work);
+
+void light_hsl_sat_work_handler(struct k_work *work);
+
+void light_xyl_work_handler(struct k_work *work);
+
+void light_lc_work_handler(struct k_work *work);
+
+void scene_recall_work_handler(struct k_work *work);
+
+void bt_mesh_server_stop_transition(struct bt_mesh_state_transition *transition);
+
+void bt_mesh_server_start_transition(struct bt_mesh_state_transition *transition);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STATE_TRANSITION_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/include/mesh/time_scene_server.h b/lib/bt/esp_ble_mesh/models/server/include/mesh/time_scene_server.h
new file mode 100644
index 00000000..7f23cdde
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/include/mesh/time_scene_server.h
@@ -0,0 +1,384 @@
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _TIME_SCENE_SERVER_H_
+#define _TIME_SCENE_SERVER_H_
+
+#include "mesh/server_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * 1. Mesh defines times based on International Atomic Time (TAI). The base
+ * representation of times is the number of seconds after 00:00:00 TAI
+ * on 2000-01-01 (that is, 1999-12-31 T23:59:28 UTC).
+ * 2. UTC: Coordinated Universal Time. For more information, please refer
+ * to https://time.is/zh/UTC
+ * 3. For the algorithm used for the transfer between TAI and UTC, please
+ * refer to Mesh Model Spec Section 5.1.1
+ */
+
+#define UNKNOWN_TAI_SECONDS 0x0000000000
+#define UNKNOWN_TAI_ZONE_CHANGE 0x0000000000
+#define UNKNOWN_TAI_DELTA_CHANGE 0x0000000000
+#define TAI_UTC_DELTA_MAX_VALUE 0x7FFF
+#define TAI_SECONDS_LEN 0x05
+#define TAI_OF_ZONE_CHANGE_LEN 0x05
+#define TAI_OF_DELTA_CHANGE_LEN 0x05
+
+#define INVALID_SCENE_NUMBER 0x0000
+#define SCENE_NUMBER_LEN 0x02
+
+#define SCHEDULE_YEAR_ANY_YEAR 0x64
+
+#define SCHEDULE_DAY_ANY_DAY 0x00
+
+#define SCHEDULE_HOUR_ANY_HOUR 0x18
+#define SCHEDULE_HOUR_ONCE_A_DAY 0x19
+
+#define SCHEDULE_SEC_ANY_OF_HOUR 0x3C
+#define SCHEDULE_SEC_EVERY_15_MIN 0x3D
+#define SCHEDULE_SEC_EVERY_20_MIN 0x3E
+#define SCHEDULE_SEC_ONCE_AN_HOUR 0x3F
+
+#define SCHEDULE_SEC_ANY_OF_MIN 0x3C
+#define SCHEDULE_SEC_EVERY_15_SEC 0x3D
+#define SCHEDULE_SEC_EVERY_20_SEC 0x3E
+#define SCHEDULE_SEC_ONCE_AN_MIN 0x3F
+
+#define SCHEDULE_ACT_TURN_OFF 0x00
+#define SCHEDULE_ACT_TURN_ON 0x01
+#define SCHEDULE_ACT_SCENE_RECALL 0x02
+#define SCHEDULE_ACT_NO_ACTION 0x0F
+
+#define SCHEDULE_SCENE_NO_SCENE 0x0000
+
+#define SCHEDULE_ENTRY_MAX_INDEX 0x0F
+
+#define TIME_NONE 0x00
+#define TIME_AUTHORITY 0x01
+#define TIME_RELAY 0x02
+#define TIME_CLIENT 0x03
+
+#define SCENE_SUCCESS 0x00
+#define SCENE_REG_FULL 0x01
+#define SCENE_NOT_FOUND 0x02
+
+/**
+ * The Time state represents the present TAI time, the current TAI-UTC Delta
+ * and local time zone offset, and the next change to each of the latter
+ * (e.g., because of a switch from winter to summer time or an announced leap
+ * second). It consists of 10 fields with a total size of 183 bits.
+ */
+struct bt_mesh_time_state {
+ struct {
+ uint8_t tai_seconds[5];
+ uint8_t subsecond;
+ uint8_t uncertainty;
+ uint8_t time_zone_offset_curr;
+ uint8_t time_zone_offset_new;
+ uint8_t tai_zone_change[5];
+ uint16_t time_authority : 1,
+ tai_utc_delta_curr : 15;
+ uint16_t tai_utc_delta_new : 15;
+ uint8_t tai_delta_change[5];
+ } time;
+ uint8_t time_role;
+};
+
+struct bt_mesh_time_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_time_state *state;
+};
+
+struct bt_mesh_time_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_time_state *state;
+};
+
+struct scene_register {
+ uint16_t scene_number;
+ uint8_t scene_type; /* Indicate the type of scene value */
+ /**
+ * Scene value may use a union to represent later, the union contains
+ * structures of all the model states which can be stored in a scene.
+ */
+ struct net_buf_simple *scene_value;
+};
+
+/**
+ * Scenes serve as memory banks for storage of states (e.g., a power level
+ * or a light level/color). Values of states of an element can be stored
+ * as a scene and can be recalled later from the scene memory.
+ *
+ * A scene is represented by a Scene Number, which is a 16-bit non-zero,
+ * mesh-wide value. (There can be a maximum of 65535 scenes in a mesh
+ * network.) The meaning of a scene, as well as the state storage container
+ * associated with it, are determined by a model.
+ *
+ * The Scenes state change may start numerous parallel model transitions.
+ * In that case, each individual model handles the transition internally.
+ *
+ * The scene transition is defined as a group of individual model transitions
+ * started by a Scene Recall operation. The scene transition is in progress
+ * when at least one transition from the group of individual model transitions
+ * is in progress.
+ */
+struct bt_mesh_scenes_state {
+ const uint16_t scene_count;
+ struct scene_register *scenes;
+
+ /**
+ * The Current Scene state is a 16-bit value that contains either the Scene
+ * Number of the currently active scene or a value of 0x0000 when no scene
+ * is active.
+ *
+ * When a Scene Store operation or a Scene Recall operation completes with
+ * success, the Current Scene state value shall be to the Scene Number used
+ * during that operation.
+ *
+ * When the Current Scene Number is deleted from a Scene Register state as a
+ * result of Scene Delete operation, the Current Scene state shall be set to
+ * 0x0000.
+ *
+ * When any of the element's state that is marked as “Stored with Scene” has
+ * changed not as a result of a Scene Recall operation, the value of the
+ * Current Scene state shall be set to 0x0000.
+ *
+ * When a scene transition is in progress, the value of the Current Scene
+ * state shall be set to 0x0000.
+ */
+ uint16_t current_scene;
+
+ /**
+ * The Target Scene state is a 16-bit value that contains the target Scene
+ * Number when a scene transition is in progress.
+ *
+ * When the scene transition is in progress and the target Scene Number is
+ * deleted from a Scene Register state as a result of Scene Delete operation,
+ * the Target Scene state shall be set to 0x0000.
+ *
+ * When the scene transition is in progress and a new Scene Number is stored
+ * in the Scene Register as a result of Scene Store operation, the Target
+ * Scene state shall be set to the new Scene Number.
+ *
+ * When the scene transition is not in progress, the value of the Target Scene
+ * state shall be set to 0x0000.
+ */
+ uint16_t target_scene;
+
+ /* Indicate the status code for the last operation */
+ uint8_t status_code;
+
+ /* Indicate if scene transition is in progress */
+ bool in_progress;
+};
+
+struct bt_mesh_scene_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_scenes_state *state;
+ struct bt_mesh_last_msg_info last;
+ struct bt_mesh_state_transition transition;
+};
+
+struct bt_mesh_scene_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_scenes_state *state;
+};
+
+struct schedule_register {
+ bool in_use;
+ uint64_t year : 7,
+ month : 12,
+ day : 5,
+ hour : 5,
+ minute : 6,
+ second : 6,
+ day_of_week : 7,
+ action : 4,
+ trans_time : 8;
+ uint16_t scene_number;
+};
+
+struct bt_mesh_scheduler_state {
+ const uint8_t schedule_count;
+ struct schedule_register *schedules; /* Up to 16 scheduled entries */
+
+ /**
+ * A recommended implementation of the Scheduler should calculate the
+ * value of the TAI Seconds of the next scheduled event and put it in
+ * a queue of scheduled events sorted by time.
+ *
+ * Every second, the first event in the queue is compared with the value
+ * of the Time state. The first event is executed if it is less than or
+ * equal to the Time state and then removed from the queue. After
+ * execution, the Repeat Flag shall be checked, and the next occurrence
+ * of the scheduled event is calculated and put in the queue.
+ *
+ * One second timeout value, and compare the first event in queue with the
+ * Time state. If it is satisfied, then execute the first event. Also the
+ * Repeat Flag need to be checked, if it is set then the event needs to
+ * be put into the end of queue.
+ *
+ * sys_slist_t event_queue;
+ *
+ * For each event_queue item, it can use the following struct:
+ * struct schedule_event {
+ * sys_snode_t node;
+ * uint8_t event_index;
+ * };
+ *
+ * Also we need a "struct k_delayed_work track_timer" which can be used to
+ * track the schedule timer and handle proper scheduled events.
+ */
+};
+
+struct bt_mesh_scheduler_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_scheduler_state *state;
+};
+
+struct bt_mesh_scheduler_setup_srv {
+ struct bt_mesh_model *model;
+ struct bt_mesh_server_rsp_ctrl rsp_ctrl;
+ struct bt_mesh_scheduler_state *state;
+};
+
+typedef union {
+ struct {
+ uint8_t tai_seconds[5];
+ uint8_t subsecond;
+ uint8_t uncertainty;
+ uint16_t time_authority : 1;
+ uint16_t tai_utc_delta_curr : 15;
+ uint8_t time_zone_offset_curr;
+ } time_set;
+ struct {
+ uint8_t tai_seconds[5];
+ uint8_t subsecond;
+ uint8_t uncertainty;
+ uint16_t time_authority : 1;
+ uint16_t tai_utc_delta_curr : 15;
+ uint8_t time_zone_offset_curr;
+ } time_status;
+ struct {
+ uint8_t time_zone_offset_new;
+ uint8_t tai_zone_change[5];
+ } time_zone_set;
+ struct {
+ uint16_t tai_utc_delta_new : 15;
+ uint8_t tai_delta_change[5];
+ } tai_utc_delta_set;
+ struct {
+ uint8_t role;
+ } time_role_set;
+ struct {
+ uint16_t scene_number;
+ } scene_store;
+ struct {
+ uint16_t scene_number;
+ } scene_recall;
+ struct {
+ uint16_t scene_number;
+ } scene_delete;
+ struct {
+ uint64_t index : 4,
+ year : 7,
+ month : 12,
+ day : 5,
+ hour : 5,
+ minute : 6,
+ second : 6,
+ day_of_week : 7,
+ action : 4,
+ trans_time : 8;
+ uint16_t scene_number;
+ } scheduler_act_set;
+} bt_mesh_time_scene_server_state_change_t;
+
+typedef union {
+ struct {
+ uint8_t index;
+ } scheduler_act_get;
+} bt_mesh_time_scene_server_recv_get_msg_t;
+
+typedef union {
+ struct {
+ uint8_t tai_seconds[5];
+ uint8_t subsecond;
+ uint8_t uncertainty;
+ uint16_t time_authority : 1;
+ uint16_t tai_utc_delta : 15;
+ uint8_t time_zone_offset;
+ } time_set;
+ struct {
+ uint8_t time_zone_offset_new;
+ uint8_t tai_zone_change[5];
+ } time_zone_set;
+ struct {
+ uint16_t tai_utc_delta_new : 15;
+ uint16_t padding : 1;
+ uint8_t tai_delta_change[5];
+ } tai_utc_delta_set;
+ struct {
+ uint8_t time_role;
+ } time_role_set;
+ struct {
+ uint16_t scene_number;
+ } scene_store;
+ struct {
+ bool op_en;
+ uint16_t scene_number;
+ uint8_t tid;
+ uint8_t trans_time;
+ uint8_t delay;
+ } scene_recall;
+ struct {
+ uint16_t scene_number;
+ } scene_delete;
+ struct {
+ uint64_t index : 4,
+ year : 7,
+ month : 12,
+ day : 5,
+ hour : 5,
+ minute : 6,
+ second : 6,
+ day_of_week : 7,
+ action : 4,
+ trans_time : 8;
+ uint16_t scene_number;
+ } scheduler_act_set;
+} bt_mesh_time_scene_server_recv_set_msg_t;
+
+typedef union {
+ struct {
+ uint8_t tai_seconds[5];
+ uint8_t subsecond;
+ uint8_t uncertainty;
+ uint16_t time_authority : 1;
+ uint16_t tai_utc_delta : 15;
+ uint8_t time_zone_offset;
+ } time_status;
+} bt_mesh_time_scene_server_recv_status_msg_t;
+
+void bt_mesh_time_scene_server_lock(void);
+void bt_mesh_time_scene_server_unlock(void);
+
+void scene_publish(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, uint16_t opcode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TIME_SCENE_SERVER_H_ */
diff --git a/lib/bt/esp_ble_mesh/models/server/lighting_server.c b/lib/bt/esp_ble_mesh/models/server/lighting_server.c
new file mode 100644
index 00000000..6df51458
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/lighting_server.c
@@ -0,0 +1,3597 @@
+/* Bluetooth: Mesh Lighting Server Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+
+#include "btc_ble_mesh_lighting_model.h"
+
+#include "mesh/config.h"
+#include "access.h"
+#include "transport.h"
+#include "mesh/model_opcode.h"
+#include "mesh/state_transition.h"
+#include "mesh/device_property.h"
+
+#if CONFIG_BLE_MESH_LIGHTING_SERVER
+
+static bt_mesh_mutex_t light_server_lock;
+
+void bt_mesh_light_server_lock(void)
+{
+ bt_mesh_mutex_lock(&light_server_lock);
+}
+
+void bt_mesh_light_server_unlock(void)
+{
+ bt_mesh_mutex_unlock(&light_server_lock);
+}
+
+/* message handlers (Start) */
+
+/* Light Lightness Server/Setup Server message handlers */
+
+static void send_light_lightness_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 5;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS: {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_actual);
+ if (srv->actual_transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->actual_transition);
+ net_buf_simple_add_le16(msg, srv->state->target_lightness_actual);
+ net_buf_simple_add_u8(msg, srv->actual_transition.remain_time);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS: {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_linear);
+ if (srv->linear_transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->linear_transition);
+ net_buf_simple_add_le16(msg, srv->state->target_lightness_linear);
+ net_buf_simple_add_u8(msg, srv->linear_transition.remain_time);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS: {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_last);
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV) {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV) {
+ struct bt_mesh_light_lightness_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV) {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->lightness_range_min);
+ net_buf_simple_add_le16(msg, srv->state->lightness_range_max);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV) {
+ struct bt_mesh_light_lightness_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->lightness_range_min);
+ net_buf_simple_add_le16(msg, srv->state->lightness_range_max);
+ }
+ break;
+ default:
+ BT_WARN("Unknown Light Lightness status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void light_lightness_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ uint16_t opcode = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Light Lightness Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_light_lightness_status(model, ctx, false, opcode);
+}
+
+void light_lightness_publish(struct bt_mesh_model *model, uint16_t opcode)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV: {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV: {
+ struct bt_mesh_light_lightness_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness Setup Server state");
+ return;
+ }
+ break;
+ }
+ default:
+ BT_ERR("Invalid Light Lightness Server model 0x%04x", model->id);
+ return;
+ }
+
+ send_light_lightness_status(model, NULL, true, opcode);
+}
+
+static void light_lightness_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ uint16_t actual = 0U;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ actual = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lightness_set.op_en = optional,
+ .lightness_set.lightness = actual,
+ .lightness_set.tid = tid,
+ .lightness_set.trans_time = trans_time,
+ .lightness_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->actual_transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ if (actual) {
+ if (srv->state->lightness_range_min && actual < srv->state->lightness_range_min) {
+ actual = srv->state->lightness_range_min;
+ } else if (srv->state->lightness_range_max && actual > srv->state->lightness_range_max) {
+ actual = srv->state->lightness_range_max;
+ }
+ }
+ srv->state->target_lightness_actual = actual;
+
+ /**
+ * If the target state is equal to the current state, the transition shall not be
+ * started and is considered complete.
+ */
+ if (srv->state->target_lightness_actual != srv->state->lightness_actual) {
+ light_lightness_actual_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .lightness_set.lightness = srv->state->lightness_actual,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->actual_transition.timer.work.user_data) {
+ memcpy(srv->actual_transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->actual_transition.counter == 0U) {
+ srv->state->lightness_actual = srv->state->target_lightness_actual;
+ /**
+ * Whenever the Light Lightness Actual state is changed with a non-transactional
+ * message or a completed sequence of transactional messages to a non-zero value,
+ * the value of the Light Lightness Last shall be set to the value of the Light
+ * Lightness Actual.
+ */
+ if (srv->state->lightness_actual) {
+ srv->state->lightness_last = srv->state->lightness_actual;
+ }
+ }
+
+ srv->actual_transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->actual_transition);
+}
+
+static void light_lightness_linear_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ uint16_t linear = 0U;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ linear = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lightness_linear_set.op_en = optional,
+ .lightness_linear_set.lightness = linear,
+ .lightness_linear_set.tid = tid,
+ .lightness_linear_set.trans_time = trans_time,
+ .lightness_linear_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->linear_transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state->target_lightness_linear = linear;
+
+ /**
+ * If the target state is equal to the current state, the transition shall not
+ * be started and is considered complete.
+ */
+ if (srv->state->target_lightness_linear != srv->state->lightness_linear) {
+ light_lightness_linear_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .lightness_linear_set.lightness = srv->state->lightness_actual,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->linear_transition.timer.work.user_data) {
+ memcpy(srv->linear_transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->linear_transition.counter == 0U) {
+ srv->state->lightness_linear = srv->state->target_lightness_linear;
+ }
+
+ srv->linear_transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->linear_transition);
+}
+
+static void light_lightness_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lightness_setup_srv *srv = model->user_data;
+ uint16_t lightness = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lightness_default_set.lightness = lightness,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (srv->state->lightness_default != lightness) {
+ srv->state->lightness_default = lightness;
+
+ bt_mesh_light_server_state_change_t change = {
+ .lightness_default_set.lightness = lightness,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS);
+}
+
+static void light_lightness_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lightness_setup_srv *srv = model->user_data;
+ uint16_t range_min = 0U, range_max = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ range_min = net_buf_simple_pull_le16(buf);
+ range_max = net_buf_simple_pull_le16(buf);
+
+ if (range_min > range_max) {
+ BT_ERR("Range min 0x%04x is greater than range max 0x%04x",
+ range_min, range_max);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lightness_range_set.range_min = range_min,
+ .lightness_range_set.range_max = range_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ /**
+ * When a Light Lightness Setup Server receives a Light Lightness Range Set
+ * message or a Light Lightness Range Set Unacknowledged message with values
+ * that cannot be accepted, it shall set the status of the operation to a
+ * value representing the reason why the values cannot be accepted.
+ *
+ * TODO: 0x0000 for Light Range Min/Max is prohibited, but BQB test case
+ * MMDL/SR/LLNS/BI-01-C requires 'SUCCESS' when it sends a set message with
+ * Light Range Min set to 0x0000.
+ */
+#if 0
+ srv->state->status_code = BLE_MESH_RANGE_UPDATE_SUCCESS;
+#else
+ if (range_min == 0x0000) {
+ srv->state->status_code = BLE_MESH_CANNOT_SET_RANGE_MIN;
+ } else if (range_max == 0x0000) {
+ srv->state->status_code = BLE_MESH_CANNOT_SET_RANGE_MAX;
+ } else {
+ srv->state->status_code = BLE_MESH_RANGE_UPDATE_SUCCESS;
+ }
+#endif
+
+ if (range_min && srv->state->lightness_range_min != range_min) {
+ srv->state->lightness_range_min = range_min;
+ }
+
+ if (range_max && srv->state->lightness_range_max != range_max) {
+ srv->state->lightness_range_max = range_max;
+ }
+
+ bt_mesh_light_server_state_change_t change = {
+ .lightness_range_set.range_min = srv->state->lightness_range_min,
+ .lightness_range_set.range_max = srv->state->lightness_range_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET) {
+ send_light_lightness_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS);
+ }
+ send_light_lightness_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS);
+}
+
+/* Light CTL Server/Temperature Server/Setup Server message handlers */
+
+static void send_light_ctl_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 9;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS: {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness);
+ net_buf_simple_add_le16(msg, srv->state->temperature);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state->target_lightness);
+ net_buf_simple_add_le16(msg, srv->state->target_temperature);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_CTL_SRV) {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->temperature_range_min);
+ net_buf_simple_add_le16(msg, srv->state->temperature_range_max);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV) {
+ struct bt_mesh_light_ctl_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->temperature_range_min);
+ net_buf_simple_add_le16(msg, srv->state->temperature_range_max);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS: {
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_CTL_SRV) {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ net_buf_simple_add_le16(msg, srv->state->temperature_default);
+ net_buf_simple_add_le16(msg, srv->state->delta_uv_default);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV) {
+ struct bt_mesh_light_ctl_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ net_buf_simple_add_le16(msg, srv->state->temperature_default);
+ net_buf_simple_add_le16(msg, srv->state->delta_uv_default);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS: {
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->temperature);
+ net_buf_simple_add_le16(msg, srv->state->delta_uv);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state->target_temperature);
+ net_buf_simple_add_le16(msg, srv->state->target_delta_uv);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ break;
+ }
+ default:
+ BT_WARN("Unknown Light CTL status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void light_ctl_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_server_rsp_ctrl *rsp_ctrl = NULL;
+ uint16_t opcode = 0U;
+
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SRV: {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV: {
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Temperature Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ default:
+ BT_ERR("Invalid Light CTL Server model 0x%04x", model->id);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (rsp_ctrl->get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Light CTL Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_light_ctl_status(model, ctx, false, opcode);
+}
+
+void light_ctl_publish(struct bt_mesh_model *model, uint16_t opcode)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SRV: {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV: {
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Temperature Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV: {
+ struct bt_mesh_light_ctl_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Setup Server state");
+ return;
+ }
+ break;
+ }
+ default:
+ BT_ERR("Invalid Light CTL Server model 0x%04x", model->id);
+ return;
+ }
+
+ send_light_ctl_status(model, NULL, true, opcode);
+}
+
+static void light_ctl_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ uint16_t lightness = 0U, temperature = 0U;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ int16_t delta_uv = 0;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+ temperature = net_buf_simple_pull_le16(buf);
+ delta_uv = (int16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (temperature < BLE_MESH_TEMPERATURE_MIN || temperature > BLE_MESH_TEMPERATURE_MAX) {
+ BT_ERR("Invalid temperature 0x%04x", temperature);
+ return;
+ }
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .ctl_set.op_en = optional,
+ .ctl_set.lightness = lightness,
+ .ctl_set.temperature = temperature,
+ .ctl_set.delta_uv = delta_uv,
+ .ctl_set.tid = tid,
+ .ctl_set.trans_time = trans_time,
+ .ctl_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state->target_lightness = lightness;
+ if (srv->state->temperature_range_min &&
+ srv->state->temperature_range_min != BLE_MESH_TEMPERATURE_UNKNOWN &&
+ temperature < srv->state->temperature_range_min) {
+ temperature = srv->state->temperature_range_min;
+ } else if (srv->state->temperature_range_max &&
+ srv->state->temperature_range_max != BLE_MESH_TEMPERATURE_UNKNOWN &&
+ temperature > srv->state->temperature_range_max) {
+ temperature = srv->state->temperature_range_max;
+ }
+ srv->state->target_temperature = temperature;
+ srv->state->target_delta_uv = delta_uv;
+
+ if (srv->state->target_lightness != srv->state->lightness ||
+ srv->state->target_temperature != srv->state->temperature ||
+ srv->state->target_delta_uv != srv->state->delta_uv) {
+ light_ctl_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .ctl_set.lightness = srv->state->lightness,
+ .ctl_set.temperature = srv->state->temperature,
+ .ctl_set.delta_uv = srv->state->delta_uv,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->lightness = srv->state->target_lightness;
+ srv->state->temperature = srv->state->target_temperature;
+ srv->state->delta_uv = srv->state->target_delta_uv;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void light_ctl_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_ctl_setup_srv *srv = model->user_data;
+ uint16_t lightness = 0U, temperature = 0U;
+ int16_t delta_uv = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+ temperature = net_buf_simple_pull_le16(buf);
+ delta_uv = (int16_t) net_buf_simple_pull_le16(buf);
+
+ if (temperature < BLE_MESH_TEMPERATURE_MIN || temperature > BLE_MESH_TEMPERATURE_MAX) {
+ BT_ERR("Invalid temperature 0x%04x", temperature);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .ctl_default_set.lightness = lightness,
+ .ctl_default_set.temperature = temperature,
+ .ctl_default_set.delta_uv = delta_uv,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (srv->state->temperature_range_min &&
+ srv->state->temperature_range_min != BLE_MESH_TEMPERATURE_UNKNOWN &&
+ temperature < srv->state->temperature_range_min) {
+ temperature = srv->state->temperature_range_min;
+ } else if (srv->state->temperature_range_max &&
+ srv->state->temperature_range_max != BLE_MESH_TEMPERATURE_UNKNOWN &&
+ temperature > srv->state->temperature_range_max) {
+ temperature = srv->state->temperature_range_max;
+ }
+
+ srv->state->lightness_default = lightness;
+ srv->state->temperature_default = temperature;
+ srv->state->delta_uv_default = delta_uv;
+
+ bt_mesh_light_server_state_change_t change = {
+ .ctl_default_set.lightness = srv->state->lightness_default,
+ .ctl_default_set.temperature = srv->state->temperature_default,
+ .ctl_default_set.delta_uv = srv->state->delta_uv_default,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS);
+}
+
+static void light_ctl_temp_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_ctl_setup_srv *srv = model->user_data;
+ uint16_t min = 0U, max = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ min = net_buf_simple_pull_le16(buf);
+ max = net_buf_simple_pull_le16(buf);
+
+ /* This is as per 6.1.3.1 in Mesh Model Specification */
+ if (min > max ||
+ min < BLE_MESH_TEMPERATURE_MIN || (min != BLE_MESH_TEMPERATURE_UNKNOWN && min > BLE_MESH_TEMPERATURE_MAX) ||
+ max < BLE_MESH_TEMPERATURE_MIN || (max != BLE_MESH_TEMPERATURE_UNKNOWN && max > BLE_MESH_TEMPERATURE_MAX)) {
+ BT_ERR("Invalid parameter, range min 0x%04x, range max 0x%04x",
+ min, max);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .ctl_temp_range_set.range_min = min,
+ .ctl_temp_range_set.range_max = max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (min == BLE_MESH_TEMPERATURE_UNKNOWN) {
+ srv->state->status_code = BLE_MESH_CANNOT_SET_RANGE_MIN;
+ } else if (max == BLE_MESH_TEMPERATURE_UNKNOWN ) {
+ srv->state->status_code = BLE_MESH_CANNOT_SET_RANGE_MAX;
+ } else {
+ srv->state->status_code = BLE_MESH_RANGE_UPDATE_SUCCESS;
+ }
+
+ if (min != BLE_MESH_TEMPERATURE_UNKNOWN && srv->state->temperature_range_min != min) {
+ srv->state->temperature_range_min = min;
+ }
+
+ if (max != BLE_MESH_TEMPERATURE_UNKNOWN && srv->state->temperature_range_max != max) {
+ srv->state->temperature_range_max = max;
+ }
+
+ bt_mesh_light_server_state_change_t change = {
+ .ctl_temp_range_set.range_min = srv->state->temperature_range_min,
+ .ctl_temp_range_set.range_max = srv->state->temperature_range_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS);
+}
+
+static void light_ctl_temp_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ uint16_t temperature = 0U;
+ int16_t delta_uv = 0;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ temperature = net_buf_simple_pull_le16(buf);
+ delta_uv = (int16_t) net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (temperature < BLE_MESH_TEMPERATURE_MIN || temperature > BLE_MESH_TEMPERATURE_MAX) {
+ BT_ERR("Invalid temperature 0x%04x", temperature);
+ return;
+ }
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .ctl_temp_set.op_en = optional,
+ .ctl_temp_set.temperature = temperature,
+ .ctl_temp_set.delta_uv = delta_uv,
+ .ctl_temp_set.tid = tid,
+ .ctl_temp_set.trans_time = trans_time,
+ .ctl_temp_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ if (srv->state->temperature_range_min &&
+ srv->state->temperature_range_min != BLE_MESH_TEMPERATURE_UNKNOWN &&
+ temperature < srv->state->temperature_range_min) {
+ temperature = srv->state->temperature_range_min;
+ } else if (srv->state->temperature_range_max &&
+ srv->state->temperature_range_max != BLE_MESH_TEMPERATURE_UNKNOWN &&
+ temperature > srv->state->temperature_range_max) {
+ temperature = srv->state->temperature_range_max;
+ }
+ srv->state->target_temperature = temperature;
+ srv->state->target_delta_uv = delta_uv;
+
+ if (srv->state->target_temperature != srv->state->temperature ||
+ srv->state->target_delta_uv != srv->state->delta_uv) {
+ light_ctl_temp_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .ctl_temp_set.temperature = srv->state->temperature,
+ .ctl_temp_set.delta_uv = srv->state->delta_uv,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->temperature = srv->state->target_temperature;
+ srv->state->delta_uv = srv->state->target_delta_uv;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET) {
+ send_light_ctl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+ }
+ send_light_ctl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+/* Light HSL Server/Hue Server/Saturation Server/Setup Server message handlers */
+
+static void send_light_hsl_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 9;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS:
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS: {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (opcode == BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS) {
+ net_buf_simple_add_le16(msg, srv->state->lightness);
+ net_buf_simple_add_le16(msg, srv->state->hue);
+ net_buf_simple_add_le16(msg, srv->state->saturation);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ } else if (opcode == BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS) {
+ net_buf_simple_add_le16(msg, srv->state->target_lightness);
+ net_buf_simple_add_le16(msg, srv->state->target_hue);
+ net_buf_simple_add_le16(msg, srv->state->target_saturation);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_HSL_SRV) {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ net_buf_simple_add_le16(msg, srv->state->hue_default);
+ net_buf_simple_add_le16(msg, srv->state->saturation_default);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV) {
+ struct bt_mesh_light_hsl_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ net_buf_simple_add_le16(msg, srv->state->hue_default);
+ net_buf_simple_add_le16(msg, srv->state->saturation_default);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_HSL_SRV) {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->hue_range_min);
+ net_buf_simple_add_le16(msg, srv->state->hue_range_max);
+ net_buf_simple_add_le16(msg, srv->state->saturation_range_min);
+ net_buf_simple_add_le16(msg, srv->state->saturation_range_max);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV) {
+ struct bt_mesh_light_hsl_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->hue_range_min);
+ net_buf_simple_add_le16(msg, srv->state->hue_range_max);
+ net_buf_simple_add_le16(msg, srv->state->saturation_range_min);
+ net_buf_simple_add_le16(msg, srv->state->saturation_range_max);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS: {
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->hue);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state->target_hue);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS: {
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->saturation);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state->target_saturation);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ break;
+ }
+ default:
+ BT_WARN("Unknown Light HSL status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void light_hsl_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_server_rsp_ctrl *rsp_ctrl = NULL;
+ uint16_t opcode = 0U;
+
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SRV: {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV: {
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Hue Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV: {
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Saturation Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ default:
+ BT_ERR("Invalid Light HSL Server model 0x%04x", model->id);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (rsp_ctrl->get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Light HSL Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_light_hsl_status(model, ctx, false, opcode);
+}
+
+void light_hsl_publish(struct bt_mesh_model *model, uint16_t opcode)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SRV: {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV: {
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Hue Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV: {
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Saturation Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV: {
+ struct bt_mesh_light_hsl_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Setup Server state");
+ return;
+ }
+ break;
+ }
+ default:
+ BT_ERR("Invalid Light HSL Server model 0x%04x", model->id);
+ return;
+ }
+
+ send_light_hsl_status(model, NULL, true, opcode);
+}
+
+static void light_hsl_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ uint16_t lightness = 0U, hue = 0U, saturation = 0U;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+ hue = net_buf_simple_pull_le16(buf);
+ saturation = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .hsl_set.op_en = optional,
+ .hsl_set.lightness = lightness,
+ .hsl_set.hue = hue,
+ .hsl_set.saturation = saturation,
+ .hsl_set.tid = tid,
+ .hsl_set.trans_time = trans_time,
+ .hsl_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state->target_lightness = lightness;
+ if (srv->state->hue_range_min && hue < srv->state->hue_range_min) {
+ hue = srv->state->hue_range_min;
+ } else if (srv->state->hue_range_max && hue > srv->state->hue_range_max) {
+ hue = srv->state->hue_range_max;
+ }
+ srv->state->target_hue = hue;
+ if (srv->state->saturation_range_min && saturation < srv->state->saturation_range_min) {
+ saturation = srv->state->saturation_range_min;
+ } else if (srv->state->saturation_range_max && saturation > srv->state->saturation_range_max) {
+ saturation = srv->state->saturation_range_max;
+ }
+ srv->state->target_saturation = saturation;
+
+ /**
+ * If the target state is equal to the current state, the transition shall not
+ * be started and is considered complete.
+ */
+ if (srv->state->target_lightness != srv->state->lightness ||
+ srv->state->target_hue != srv->state->hue ||
+ srv->state->target_saturation != srv->state->saturation) {
+ light_hsl_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .hsl_set.lightness = srv->state->lightness,
+ .hsl_set.hue = srv->state->hue,
+ .hsl_set.saturation = srv->state->saturation,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->lightness = srv->state->target_lightness;
+ srv->state->hue = srv->state->target_hue;
+ srv->state->saturation = srv->state->target_saturation;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void light_hsl_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_hsl_setup_srv *srv = model->user_data;
+ uint16_t lightness = 0U, hue = 0U, saturation = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+ hue = net_buf_simple_pull_le16(buf);
+ saturation = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .hsl_default_set.lightness = lightness,
+ .hsl_default_set.hue = hue,
+ .hsl_default_set.saturation = saturation,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (srv->state->hue_range_min && hue < srv->state->hue_range_min) {
+ hue = srv->state->hue_range_min;
+ } else if (srv->state->hue_range_max && hue > srv->state->hue_range_max) {
+ hue = srv->state->hue_range_max;
+ }
+
+ if (srv->state->saturation_range_min && saturation < srv->state->saturation_range_min) {
+ saturation = srv->state->saturation_range_min;
+ } else if (srv->state->saturation_range_max && saturation > srv->state->saturation_range_max) {
+ saturation = srv->state->saturation_range_max;
+ }
+
+ srv->state->lightness_default = lightness;
+ srv->state->hue_default = hue;
+ srv->state->saturation_default = saturation;
+
+ bt_mesh_light_server_state_change_t change = {
+ .hsl_default_set.lightness = srv->state->lightness_default,
+ .hsl_default_set.hue = srv->state->hue_default,
+ .hsl_default_set.saturation = srv->state->saturation_default,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS);
+}
+
+static void light_hsl_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_hsl_setup_srv *srv = model->user_data;
+ uint16_t hue_min = 0U, hue_max = 0U, saturation_min = 0U, saturation_max = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ hue_min = net_buf_simple_pull_le16(buf);
+ hue_max = net_buf_simple_pull_le16(buf);
+ saturation_min = net_buf_simple_pull_le16(buf);
+ saturation_max = net_buf_simple_pull_le16(buf);
+
+ if (hue_min > hue_max) {
+ BT_ERR("Invalid parameter, hue min 0x%04x, hue max 0x%04x",
+ hue_min, hue_max);
+ return;
+ }
+
+ if (saturation_min > saturation_max) {
+ BT_ERR("Invalid parameter, saturation min 0x%04x, saturation max 0x%04x",
+ saturation_min, saturation_max);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .hsl_range_set.hue_range_min = hue_min,
+ .hsl_range_set.hue_range_max = hue_max,
+ .hsl_range_set.sat_range_min = saturation_min,
+ .hsl_range_set.sat_range_max = saturation_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ srv->state->status_code = BLE_MESH_RANGE_UPDATE_SUCCESS;
+ srv->state->hue_range_min = hue_min;
+ srv->state->hue_range_max = hue_max;
+ srv->state->saturation_range_min = saturation_min;
+ srv->state->saturation_range_max = saturation_max;
+
+ bt_mesh_light_server_state_change_t change = {
+ .hsl_range_set.hue_range_min = srv->state->hue_range_min,
+ .hsl_range_set.hue_range_max = srv->state->hue_range_max,
+ .hsl_range_set.sat_range_min = srv->state->saturation_range_min,
+ .hsl_range_set.sat_range_max = srv->state->saturation_range_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS);
+}
+
+static void light_hsl_hue_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ uint16_t hue = 0U;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ hue = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .hsl_hue_set.op_en = optional,
+ .hsl_hue_set.hue = hue,
+ .hsl_hue_set.tid = tid,
+ .hsl_hue_set.trans_time = trans_time,
+ .hsl_hue_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ if (srv->state->hue_range_min && hue < srv->state->hue_range_min) {
+ hue = srv->state->hue_range_min;
+ } else if (srv->state->hue_range_max && hue > srv->state->hue_range_max) {
+ hue = srv->state->hue_range_max;
+ }
+ srv->state->target_hue = hue;
+
+ /**
+ * If the target state is equal to the current state, the transition shall not
+ * be started and is considered complete.
+ */
+ if (srv->state->target_hue != srv->state->hue) {
+ light_hsl_hue_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .hsl_hue_set.hue = srv->state->hue,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->hue = srv->state->target_hue;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void light_hsl_sat_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ uint16_t saturation = 0U;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ saturation = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .hsl_saturation_set.op_en = optional,
+ .hsl_saturation_set.saturation = saturation,
+ .hsl_saturation_set.tid = tid,
+ .hsl_saturation_set.trans_time = trans_time,
+ .hsl_saturation_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ if (srv->state->saturation_range_min && saturation < srv->state->saturation_range_min) {
+ saturation = srv->state->saturation_range_min;
+ } else if (srv->state->saturation_range_max && saturation > srv->state->saturation_range_max) {
+ saturation = srv->state->saturation_range_max;
+ }
+ srv->state->target_saturation = saturation;
+
+ /**
+ * If the target state is equal to the current state, the transition shall not
+ * be started and is considered complete.
+ */
+ if (srv->state->target_saturation != srv->state->saturation) {
+ light_hsl_sat_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .hsl_saturation_set.saturation = srv->state->saturation,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->saturation = srv->state->target_saturation;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET) {
+ send_light_hsl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+ }
+ send_light_hsl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+/* Light xyL Server/Setup Server message handlers */
+
+static void send_light_xyl_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 9;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS:
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS: {
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ if (opcode == BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS) {
+ net_buf_simple_add_le16(msg, srv->state->lightness);
+ net_buf_simple_add_le16(msg, srv->state->x);
+ net_buf_simple_add_le16(msg, srv->state->y);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ } else if (opcode == BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS) {
+ net_buf_simple_add_le16(msg, srv->state->target_lightness);
+ net_buf_simple_add_le16(msg, srv->state->target_x);
+ net_buf_simple_add_le16(msg, srv->state->target_y);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_XYL_SRV) {
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ net_buf_simple_add_le16(msg, srv->state->x_default);
+ net_buf_simple_add_le16(msg, srv->state->y_default);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV) {
+ struct bt_mesh_light_xyl_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->lightness_default);
+ net_buf_simple_add_le16(msg, srv->state->x_default);
+ net_buf_simple_add_le16(msg, srv->state->y_default);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_XYL_SRV) {
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->x_range_min);
+ net_buf_simple_add_le16(msg, srv->state->x_range_max);
+ net_buf_simple_add_le16(msg, srv->state->y_range_min);
+ net_buf_simple_add_le16(msg, srv->state->y_range_max);
+ } else if (model->id == BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV) {
+ struct bt_mesh_light_xyl_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ net_buf_simple_add_le16(msg, srv->state->x_range_min);
+ net_buf_simple_add_le16(msg, srv->state->x_range_max);
+ net_buf_simple_add_le16(msg, srv->state->y_range_min);
+ net_buf_simple_add_le16(msg, srv->state->y_range_max);
+ }
+ break;
+ default:
+ BT_WARN("Unknown Light xyL status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void light_xyl_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ uint16_t opcode = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Light xyL Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_light_xyl_status(model, ctx, false, opcode);
+}
+
+void light_xyl_publish(struct bt_mesh_model *model, uint16_t opcode)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_XYL_SRV: {
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light xyL Server state");
+ return;
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV: {
+ struct bt_mesh_light_xyl_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light xyL Setup Server state");
+ return;
+ }
+ break;
+ }
+ default:
+ BT_ERR("Invalid Light xyL Server model 0x%04x", model->id);
+ return;
+ }
+
+ send_light_xyl_status(model, NULL, true, opcode);
+}
+
+static void light_xyl_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ uint16_t lightness = 0U, x = 0U, y = 0U;
+ bool optional = false;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+ x = net_buf_simple_pull_le16(buf);
+ y = net_buf_simple_pull_le16(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .xyl_set.op_en = optional,
+ .xyl_set.lightness = lightness,
+ .xyl_set.x = x,
+ .xyl_set.y = y,
+ .xyl_set.tid = tid,
+ .xyl_set.trans_time = trans_time,
+ .xyl_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_XYL_SET) {
+ send_light_xyl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+ }
+ send_light_xyl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state->target_lightness = lightness;
+ if (srv->state->x_range_min && x < srv->state->x_range_min) {
+ x = srv->state->x_range_min;
+ } else if (srv->state->x_range_max && x > srv->state->x_range_max) {
+ x = srv->state->x_range_max;
+ }
+ srv->state->target_x = x;
+ if (srv->state->y_range_min && y < srv->state->y_range_min) {
+ y = srv->state->y_range_min;
+ } else if (srv->state->y_range_max && y > srv->state->y_range_max) {
+ y = srv->state->y_range_max;
+ }
+ srv->state->target_y = y;
+
+ /**
+ * If the target state is equal to the current state, the transition shall not
+ * be started and is considered complete.
+ */
+ if (srv->state->target_lightness != srv->state->lightness ||
+ srv->state->target_x != srv->state->x ||
+ srv->state->target_y != srv->state->y) {
+ light_xyl_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .xyl_set.lightness = srv->state->lightness,
+ .xyl_set.x = srv->state->x,
+ .xyl_set.y = srv->state->y,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_XYL_SET) {
+ send_light_xyl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+ }
+ send_light_xyl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->lightness = srv->state->target_lightness;
+ srv->state->x = srv->state->target_x;
+ srv->state->y = srv->state->target_y;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_XYL_SET) {
+ send_light_xyl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+ }
+ send_light_xyl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void light_xyl_default_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_xyl_setup_srv *srv = model->user_data;
+ uint16_t lightness = 0U, x = 0U, y = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ lightness = net_buf_simple_pull_le16(buf);
+ x = net_buf_simple_pull_le16(buf);
+ y = net_buf_simple_pull_le16(buf);
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .xyl_default_set.lightness = lightness,
+ .xyl_default_set.x = x,
+ .xyl_default_set.y = y,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (srv->state->x_range_min && x < srv->state->x_range_min) {
+ x = srv->state->x_range_min;
+ } else if (srv->state->x_range_max && x > srv->state->x_range_max) {
+ x = srv->state->x_range_max;
+ }
+
+ if (srv->state->y_range_min && y < srv->state->y_range_min) {
+ y = srv->state->y_range_min;
+ } else if (srv->state->y_range_max && y > srv->state->y_range_max) {
+ y = srv->state->y_range_max;
+ }
+
+ srv->state->lightness_default = lightness;
+ srv->state->x_default = x;
+ srv->state->y_default = y;
+
+ bt_mesh_light_server_state_change_t change = {
+ .xyl_default_set.lightness = srv->state->lightness_default,
+ .xyl_default_set.x = srv->state->x_default,
+ .xyl_default_set.y = srv->state->y_default,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET) {
+ send_light_xyl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS);
+ }
+ send_light_xyl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS);
+}
+
+static void light_xyl_range_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_xyl_setup_srv *srv = model->user_data;
+ uint16_t x_min = 0U, x_max = 0U, y_min = 0U, y_max = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ x_min = net_buf_simple_pull_le16(buf);
+ x_max = net_buf_simple_pull_le16(buf);
+ y_min = net_buf_simple_pull_le16(buf);
+ y_max = net_buf_simple_pull_le16(buf);
+
+ if (x_min > x_max) {
+ BT_ERR("Invalid parameter, x min 0x%04x, x max 0x%04x",
+ x_min, x_max);
+ return;
+ }
+
+ if (y_min > y_max) {
+ BT_ERR("Invalid parameter, y min 0x%04x, y max 0x%04x",
+ y_min, y_max);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .xyl_range_set.x_range_min = x_min,
+ .xyl_range_set.x_range_max = x_max,
+ .xyl_range_set.y_range_min = y_min,
+ .xyl_range_set.y_range_max = y_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ srv->state->status_code = BLE_MESH_RANGE_UPDATE_SUCCESS;
+ srv->state->x_range_min = x_min;
+ srv->state->x_range_max = x_max;
+ srv->state->y_range_min = y_min;
+ srv->state->y_range_max = y_max;
+
+ bt_mesh_light_server_state_change_t change = {
+ .xyl_range_set.x_range_min = srv->state->x_range_min,
+ .xyl_range_set.x_range_max = srv->state->x_range_max,
+ .xyl_range_set.y_range_min = srv->state->y_range_min,
+ .xyl_range_set.y_range_max = srv->state->y_range_max,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET) {
+ send_light_xyl_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS);
+ }
+ send_light_xyl_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS);
+}
+
+/* Light LC Server/Setup Server message handlers */
+static void send_light_lc_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 2 + 3;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS:
+ net_buf_simple_add_u8(msg, srv->lc->state.mode);
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS:
+ net_buf_simple_add_u8(msg, srv->lc->state.occupancy_mode);
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS:
+ net_buf_simple_add_u8(msg, srv->lc->state.light_onoff);
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_u8(msg, srv->lc->state.target_light_onoff);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+ break;
+ default:
+ BT_WARN("Unknown Light LC status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void light_lc_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ uint16_t opcode = 0U;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET:
+ opcode = BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Light LC Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_light_lc_status(model, ctx, false, opcode);
+}
+
+void light_lc_publish(struct bt_mesh_model *model, uint16_t opcode)
+{
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ send_light_lc_status(model, NULL, true, opcode);
+}
+
+static void light_lc_mode_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ uint8_t mode = 0U;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ mode = net_buf_simple_pull_u8(buf);
+ if (mode > BLE_MESH_STATE_ON) {
+ BT_ERR("Invalid LC Mode 0x%02x", mode);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lc_mode_set.mode = mode,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ srv->lc->state.mode = mode;
+
+ bt_mesh_light_server_state_change_t change = {
+ .lc_mode_set.mode = srv->lc->state.mode,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET) {
+ send_light_lc_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS);
+ }
+ send_light_lc_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS);
+}
+
+static void light_lc_om_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ uint8_t om = 0U;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ om = net_buf_simple_pull_u8(buf);
+ if (om > BLE_MESH_STATE_ON) {
+ BT_ERR("Invalid LC Occupancy Mode 0x%02x", om);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lc_om_set.mode = om,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ srv->lc->state.occupancy_mode = om;
+
+ bt_mesh_light_server_state_change_t change = {
+ .lc_om_set.mode = srv->lc->state.occupancy_mode,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET) {
+ send_light_lc_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS);
+ }
+ send_light_lc_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS);
+}
+
+static void light_lc_light_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ bool optional = false;
+ uint8_t onoff = 0U;
+ int64_t now = 0;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ onoff = net_buf_simple_pull_u8(buf);
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lc_light_onoff_set.op_en = optional,
+ .lc_light_onoff_set.light_onoff = onoff,
+ .lc_light_onoff_set.tid = tid,
+ .lc_light_onoff_set.trans_time = trans_time,
+ .lc_light_onoff_set.delay = delay,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET) {
+ send_light_lc_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+ }
+ send_light_lc_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->lc->state.target_light_onoff = onoff;
+
+ if (srv->lc->state.target_light_onoff != srv->lc->state.light_onoff) {
+ light_lc_tt_values(srv, trans_time, delay);
+ } else {
+ bt_mesh_light_server_state_change_t change = {
+ .lc_light_onoff_set.onoff = srv->lc->state.light_onoff,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET) {
+ send_light_lc_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+ }
+ send_light_lc_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->lc->state.light_onoff = srv->lc->state.target_light_onoff;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET) {
+ send_light_lc_status(model, ctx, false, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+ }
+ send_light_lc_status(model, NULL, true, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+
+ bt_mesh_light_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void light_lc_sensor_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ /**
+ * When a Light LC Server receives a Sensor Status message, and if the message
+ * Raw field contains a Raw Value for the Motion Sensed Property, and the value
+ * is greater than 0, or a Raw Value for the People Count Property, and the
+ * value is greater than 0, or a Raw Value for the Presence Detected Property,
+ * and the value is greater than 0, then it shall set the Light LC Occupancy
+ * state to 0b1.
+ * If the message Raw field contains a Raw Value for the Time Since Motion Sensed
+ * device property, which represents a value less than or equal to the value of
+ * the Light LC Occupancy Delay state, it shall delay setting the Light LC Occupancy
+ * state to 0b1 by the difference between the value of the Light LC Occupancy Delay
+ * state and the received Time Since Motion value.
+ * When a Light LC Server receives a Sensor Status message, and if the message Raw
+ * field contains a Raw Value for the Present Ambient Light Level device property,
+ * it shall set the Light LC Ambient LuxLevel state to the Represented Value of the
+ * received Present Ambient Light Level.
+ *
+ * Motion Sensed: 1 octet, 0x0042
+ * People Count: 2 octets, 0x004C
+ * Presence Detected: 1 octet, 0x004D
+ *
+ * Time Since Motion Sensed: 2 octets, 0x0068
+ *
+ * Present Ambient Light Level: 4 octets, 0x004E
+ */
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ bt_mesh_light_server_state_change_t change = {0};
+ uint16_t mpid = 0U, prop_id = 0U;
+ uint8_t length = 0U;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ if (srv->rsp_ctrl.status_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_status_msg_t status = {
+ .sensor_status.data = buf,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_STATUS_MSG,
+ model, ctx, (const uint8_t *)&status, sizeof(status));
+ return;
+ }
+
+ mpid = net_buf_simple_pull_le16(buf);
+ if (mpid & BIT(0)) {
+ length = (uint8_t)((mpid & 0xff) >> 1);
+ uint8_t msb = net_buf_simple_pull_u8(buf);
+ prop_id = (uint16_t)(msb << 8) | (uint16_t)(mpid >> 8);
+ } else {
+ length = (uint8_t)((mpid & 0x1f) >> 1);
+ prop_id = (uint16_t)(mpid >> 5);
+ }
+
+ change.sensor_status.property_id = prop_id;
+
+ switch (prop_id) {
+ case BLE_MESH_MOTION_SENSED: {
+ if (length != BLE_MESH_MOTION_SENSED_LEN || length != buf->len) {
+ BT_WARN("Invalid Motion Sensed Property length %d", length);
+ return;
+ }
+ uint8_t val = net_buf_simple_pull_u8(buf);
+ if (val > 0) {
+ srv->lc->state.occupancy = BLE_MESH_STATE_ON;
+
+ change.sensor_status.state.occupancy = srv->lc->state.occupancy;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ break;
+ }
+ case BLE_MESH_PEOPLE_COUNT: {
+ if (length != BLE_MESH_PEOPLE_COUNT_LEN || length != buf->len) {
+ BT_WARN("Invalid Motion Sensed Property length %d", length);
+ return;
+ }
+ uint16_t val = net_buf_simple_pull_le16(buf);
+ if (val > 0) {
+ srv->lc->state.occupancy = BLE_MESH_STATE_ON;
+
+ change.sensor_status.state.occupancy = srv->lc->state.occupancy;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ break;
+ }
+ case BLE_MESH_PRESENCE_DETECTED: {
+ if (length != BLE_MESH_PRESENCE_DETECTED_LEN || length != buf->len) {
+ BT_WARN("Invalid Motion Sensed Property length %d", length);
+ return;
+ }
+ uint8_t val = net_buf_simple_pull_u8(buf);
+ if (val > 0) {
+ srv->lc->state.occupancy = BLE_MESH_STATE_ON;
+
+ change.sensor_status.state.occupancy = srv->lc->state.occupancy;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ break;
+ }
+ case BLE_MESH_TIME_SINCE_MOTION_SENSED: {
+ if (length != BLE_MESH_TIME_SINCE_MOTION_SENSED_LEN || length != buf->len) {
+ BT_WARN("Invalid Motion Sensed Property length %d", length);
+ return;
+ }
+ uint16_t val = net_buf_simple_pull_le16(buf);
+ if (val <= srv->lc->prop_state.time_occupancy_delay) {
+ srv->lc->prop_state.set_occupancy_to_1_delay =
+ srv->lc->prop_state.time_occupancy_delay - val;
+
+ change.sensor_status.state.set_occupancy_to_1_delay = srv->lc->prop_state.set_occupancy_to_1_delay;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ break;
+ }
+ case BLE_MESH_PRESENT_AMBIENT_LIGHT_LEVEL: {
+ /**
+ * Present Ambient Light Level device property is 4 octets, but ambient
+ * luxlevel length is 3 octets, and other devices may send Sensor Status
+ * which only contains 3 octets just for Light LC Server.
+ * Here we just check if the length is larger than 3.
+ */
+ if (buf->len < 3) {
+ BT_WARN("Invalid Motion Sensed Property length %d", buf->len);
+ return;
+ }
+ uint16_t lsb = net_buf_simple_pull_le16(buf);
+ uint8_t msb = net_buf_simple_pull_u8(buf);
+ srv->lc->state.ambient_luxlevel = (msb << 16) | lsb;
+
+ change.sensor_status.state.ambient_luxlevel = srv->lc->state.ambient_luxlevel;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static uint8_t *get_light_lc_prop_val(struct bt_mesh_model *model, uint16_t prop_id)
+{
+ struct bt_mesh_light_lc_setup_srv *srv = model->user_data;
+ uint8_t *val = NULL;
+
+ switch (prop_id) {
+ case BLE_MESH_LIGHT_CONTROL_TIME_OCCUPANCY_DELAY:
+ val = (uint8_t *)&srv->lc->prop_state.time_occupancy_delay;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_TIME_FADE_ON:
+ val = (uint8_t *)&srv->lc->prop_state.time_fade_on;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_TIME_RUN_ON:
+ val = (uint8_t *)&srv->lc->prop_state.time_run_on;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_TIME_FADE:
+ val = (uint8_t *)&srv->lc->prop_state.time_fade;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_TIME_PROLONG:
+ val = (uint8_t *)&srv->lc->prop_state.time_prolong;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_TIME_FADE_STANDBY_AUTO:
+ val = (uint8_t *)&srv->lc->prop_state.time_fade_standby_auto;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_TIME_FADE_STANDBY_MANUAL:
+ val = (uint8_t *)&srv->lc->prop_state.time_fade_standby_manual;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_LIGHTNESS_ON:
+ val = (uint8_t *)&srv->lc->prop_state.lightness_on;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_LIGHTNESS_PROLONG:
+ val = (uint8_t *)&srv->lc->prop_state.lightness_prolong;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_LIGHTNESS_STANDBY:
+ val = (uint8_t *)&srv->lc->prop_state.lightness_standby;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_AMBIENT_LUXLEVEL_ON:
+ val = (uint8_t *)&srv->lc->prop_state.ambient_luxlevel_on;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_AMBIENT_LUXLEVEL_PROLONG:
+ val = (uint8_t *)&srv->lc->prop_state.ambient_luxlevel_prolong;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_AMBIENT_LUXLEVEL_STANDBY:
+ val = (uint8_t *)&srv->lc->prop_state.ambient_luxlevel_standby;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_REGULATOR_KIU:
+ val = (uint8_t *)&srv->lc->prop_state.regulator_kiu;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_REGULATOR_KID:
+ val = (uint8_t *)&srv->lc->prop_state.regulator_kid;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_REGULATOR_KPU:
+ val = (uint8_t *)&srv->lc->prop_state.regulator_kpu;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_REGULATOR_KPD:
+ val = (uint8_t *)&srv->lc->prop_state.regulator_kpd;
+ break;
+ case BLE_MESH_LIGHT_CONTROL_REGULATOR_ACCURACY:
+ val = (uint8_t *)&srv->lc->prop_state.regulator_accuracy;
+ break;
+ }
+
+ return val;
+}
+
+uint8_t *bt_mesh_get_lc_prop_value(struct bt_mesh_model *model, uint16_t prop_id)
+{
+ if (model == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return NULL;
+ }
+
+ return get_light_lc_prop_val(model, prop_id);
+}
+
+static void send_light_lc_prop_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t prop_id, bool publish)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 1 + 2 + 4;
+ uint8_t *prop_val = NULL;
+
+ prop_val = get_light_lc_prop_val(model, prop_id);
+ if (prop_val == NULL) {
+ BT_ERR("Failed to get Light LC Property value");
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS);
+ net_buf_simple_add_le16(msg, prop_id);
+ net_buf_simple_add_mem(msg, prop_val, bt_mesh_get_dev_prop_len(prop_id));
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void light_lc_prop_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lc_setup_srv *srv = model->user_data;
+ uint16_t prop_id = 0U;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id < 0x002B || prop_id > 0x003C) {
+ BT_ERR("Invalid Light LC Property ID 0x%04x", prop_id);
+ return;
+ }
+
+ /* Callback the received message to the application layer */
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_get_msg_t get = {
+ .lc_property_get.id = net_buf_simple_pull_le16(buf),
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ return;
+ }
+
+ send_light_lc_prop_status(model, ctx, prop_id, false);
+}
+
+static void light_lc_prop_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_light_lc_setup_srv *srv = model->user_data;
+ uint8_t *prop_val = NULL, expect_len = 0U;
+ uint16_t prop_id = 0U;
+
+ if (srv == NULL || srv->lc == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id < 0x002B || prop_id > 0x003C) {
+ BT_ERR("Invalid Light LC Property ID 0x%04x", prop_id);
+ return;
+ }
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_light_server_recv_set_msg_t set = {
+ .lc_property_set.id = net_buf_simple_pull_le16(buf),
+ .lc_property_set.value = buf,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ expect_len = bt_mesh_get_dev_prop_len(prop_id);
+ if (buf->len != expect_len) {
+ BT_ERR("Invalid Light LC Property 0x%04x length, expect %d, actual %d",
+ prop_id, expect_len, buf->len);
+ return;
+ }
+
+ prop_val = get_light_lc_prop_val(model, prop_id);
+ if (prop_val == NULL) {
+ BT_ERR("Failed to get Light LC Property value");
+ return;
+ }
+
+ memcpy(prop_val, buf->data, buf->len);
+
+ bt_mesh_light_server_state_change_t change = {
+ .lc_property_set.id = prop_id,
+ .lc_property_set.value = buf,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET) {
+ send_light_lc_prop_status(model, ctx, prop_id, false);
+ }
+ send_light_lc_prop_status(model, ctx, prop_id, true);
+}
+
+/* message handlers (End) */
+
+/* Mapping of message handlers for Light Lightness Server (0x1300) */
+const struct bt_mesh_model_op bt_mesh_light_lightness_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET, 0, light_lightness_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET, 3, light_lightness_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK, 3, light_lightness_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET, 0, light_lightness_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET, 3, light_lightness_linear_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK, 3, light_lightness_linear_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET, 0, light_lightness_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET, 0, light_lightness_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET, 0, light_lightness_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light Lightness Setup Server (0x1301) */
+const struct bt_mesh_model_op bt_mesh_light_lightness_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET, 2, light_lightness_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK, 2, light_lightness_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET, 4, light_lightness_range_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK, 4, light_lightness_range_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Server (0x1303) */
+const struct bt_mesh_model_op bt_mesh_light_ctl_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_GET, 0, light_ctl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_SET, 7, light_ctl_set },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK, 7, light_ctl_set },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET, 0, light_ctl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET, 0, light_ctl_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Setup Server (0x1304) */
+const struct bt_mesh_model_op bt_mesh_light_ctl_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET, 6, light_ctl_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK, 6, light_ctl_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET, 4, light_ctl_temp_range_set },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK, 4, light_ctl_temp_range_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light CTL Temperature Server (0x1306) */
+const struct bt_mesh_model_op bt_mesh_light_ctl_temp_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET, 0, light_ctl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET, 5, light_ctl_temp_set },
+ { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK, 5, light_ctl_temp_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light HSL Server (0x1307) */
+const struct bt_mesh_model_op bt_mesh_light_hsl_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_GET, 0, light_hsl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_SET, 7, light_hsl_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK, 7, light_hsl_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET, 0, light_hsl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET, 0, light_hsl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET, 0, light_hsl_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light HSL Setup Server (0x1308) */
+const struct bt_mesh_model_op bt_mesh_light_hsl_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET, 6, light_hsl_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK, 6, light_hsl_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET, 8, light_hsl_range_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK, 8, light_hsl_range_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light HSL Hue Server (0x130A) */
+const struct bt_mesh_model_op bt_mesh_light_hsl_hue_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET, 0, light_hsl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET, 3, light_hsl_hue_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK, 3, light_hsl_hue_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light HSL Saturation Server (0x130B) */
+const struct bt_mesh_model_op bt_mesh_light_hsl_sat_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET, 0, light_hsl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET, 3, light_hsl_sat_set },
+ { BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK, 3, light_hsl_sat_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light xyL Server (0x130C) */
+const struct bt_mesh_model_op bt_mesh_light_xyl_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_GET, 0, light_xyl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_SET, 7, light_xyl_set },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK, 7, light_xyl_set },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET, 0, light_xyl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET, 0, light_xyl_get },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET, 0, light_xyl_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light xyL Setup Server (0x130D) */
+const struct bt_mesh_model_op bt_mesh_light_xyl_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET, 6, light_xyl_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK, 6, light_xyl_default_set },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET, 8, light_xyl_range_set },
+ { BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK, 8, light_xyl_range_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light LC Server (0x130F) */
+const struct bt_mesh_model_op bt_mesh_light_lc_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET, 0, light_lc_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET, 1, light_lc_mode_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK, 1, light_lc_mode_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET, 0, light_lc_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET, 1, light_lc_om_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK, 1, light_lc_om_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET, 0, light_lc_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET, 2, light_lc_light_onoff_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK, 2, light_lc_light_onoff_set },
+ { BLE_MESH_MODEL_OP_SENSOR_STATUS, 3, light_lc_sensor_status },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Light LC Setup Server (0x1310) */
+const struct bt_mesh_model_op bt_mesh_light_lc_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET, 2, light_lc_prop_get },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET, 3, light_lc_prop_set },
+ { BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK, 3, light_lc_prop_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+static int light_server_init(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Lighting Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV: {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->actual_transition.timer.work);
+ bt_mesh_server_alloc_ctx(&srv->linear_transition.timer.work);
+ k_delayed_work_init(&srv->actual_transition.timer, light_lightness_actual_work_handler);
+ k_delayed_work_init(&srv->linear_transition.timer, light_lightness_linear_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV: {
+ struct bt_mesh_light_lightness_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SRV: {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_ctl_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV: {
+ struct bt_mesh_light_ctl_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV: {
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_ctl_temp_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SRV: {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_hsl_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV: {
+ struct bt_mesh_light_hsl_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV: {
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_hsl_hue_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV: {
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_hsl_sat_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_XYL_SRV: {
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light xyL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_xyl_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV: {
+ struct bt_mesh_light_xyl_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light xyL State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_LC_SRV: {
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ if (srv->lc == NULL) {
+ BT_ERR("Invalid Light LC State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, light_lc_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_LC_SETUP_SRV: {
+ struct bt_mesh_light_lc_setup_srv *srv = model->user_data;
+ if (srv->lc == NULL) {
+ BT_ERR("Invalid Light LC State");
+ return -EINVAL;
+ }
+ srv->model = model;
+ break;
+ }
+ default:
+ BT_WARN("Unknown Light Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ bt_mesh_mutex_create(&light_server_lock);
+
+ return 0;
+}
+
+static int light_lightness_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light Lightness Server has no publication support");
+ return -EINVAL;
+ }
+
+ /* When this model is present on an Element, the corresponding Light Lightness
+ * Setup Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV) == NULL) {
+ BT_WARN("Light Lightness Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return light_server_init(model);
+}
+
+static int light_lightness_setup_srv_init(struct bt_mesh_model *model)
+{
+ return light_server_init(model);
+}
+
+static int light_ctl_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light CTL Server has no publication support");
+ return -EINVAL;
+ }
+
+ /**
+ * When this model is present on an Element, the corresponding Light CTL
+ * Temperature Server model and the corresponding Light CTL Setup Server
+ * model shall also be present.
+ * The model requires two elements: the main element and the Temperature
+ * element. The Temperature element contains the corresponding Light CTL
+ * Temperature Server model.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV) == NULL) {
+ BT_WARN("Light CTL Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ if (bt_mesh_elem_count() < 2) {
+ BT_WARN("Light CTL Server requires two elements");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return light_server_init(model);
+}
+
+static int light_ctl_setup_srv_init(struct bt_mesh_model *model)
+{
+ return light_server_init(model);
+}
+
+static int light_ctl_temp_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light CTL Temperature Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_init(model);
+}
+
+static int light_hsl_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light HSL Server has no publication support");
+ return -EINVAL;
+ }
+
+ /**
+ * When this model is present on an Element, the corresponding Light HSL Hue
+ * Server model and the corresponding Light HSL Saturation Server model and
+ * the corresponding Light HSL Setup Server model shall also be present.
+ * The model requires three elements: the main element and the Hue element
+ * and the Saturation element. The Hue element contains the corresponding
+ * Light HSL Hue Server model, and the Saturation element contains the
+ * corresponding Light HSL Saturation Server model.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV) == NULL) {
+ BT_WARN("Light HSL Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ if (bt_mesh_elem_count() < 3) {
+ BT_WARN("Light HSL Server requires three elements");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return light_server_init(model);
+}
+
+static int light_hsl_setup_srv_init(struct bt_mesh_model *model)
+{
+ return light_server_init(model);
+}
+
+static int light_hsl_hue_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light HSL Hue Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_init(model);
+}
+
+static int light_hsl_sat_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light HSL Saturation Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_init(model);
+}
+
+static int light_xyl_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light xyL Server has no publication support");
+ return -EINVAL;
+ }
+
+ /**
+ * When this model is present on an Element, the corresponding Light xyL
+ * Setup Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV) == NULL) {
+ BT_WARN("Light xyL Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return light_server_init(model);
+}
+
+static int light_xyl_setup_srv_init(struct bt_mesh_model *model)
+{
+ return light_server_init(model);
+}
+
+static int light_lc_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light LC Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_init(model);
+}
+
+static int light_lc_setup_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light LC Setup Server has no publication support");
+ return -EINVAL;
+ }
+
+ /**
+ * When this model is present on an Element, the corresponding Light LC
+ * Setup Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_LIGHT_LC_SETUP_SRV) == NULL) {
+ BT_WARN("Light LC Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return light_server_init(model);
+}
+
+#if CONFIG_BLE_MESH_DEINIT
+static int light_server_deinit(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Lighting Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV: {
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->actual_transition.timer.work);
+ bt_mesh_server_free_ctx(&srv->linear_transition.timer.work);
+ k_delayed_work_free(&srv->actual_transition.timer);
+ k_delayed_work_free(&srv->linear_transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SRV: {
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV: {
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SRV: {
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV: {
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV: {
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_XYL_SRV: {
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light xyL State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_LC_SRV: {
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ if (srv->lc == NULL) {
+ BT_ERR("Invalid Light LC State");
+ return -EINVAL;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_LIGHT_LC_SETUP_SRV:
+ break;
+ default:
+ BT_WARN("Unknown Light Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ bt_mesh_mutex_free(&light_server_lock);
+
+ return 0;
+}
+
+static int light_lightness_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light Lightness Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_lightness_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return light_server_deinit(model);
+}
+
+static int light_ctl_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light CTL Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_ctl_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return light_server_deinit(model);
+}
+
+static int light_ctl_temp_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light CTL Temperature Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_hsl_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light HSL Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_hsl_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return light_server_deinit(model);
+}
+
+static int light_hsl_hue_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light HSL Hue Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_hsl_sat_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light HSL Saturation Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_xyl_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light xyL Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_xyl_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return light_server_deinit(model);
+}
+
+static int light_lc_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light LC Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+
+static int light_lc_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Light LC Setup Server has no publication support");
+ return -EINVAL;
+ }
+
+ return light_server_deinit(model);
+}
+#endif /* CONFIG_BLE_MESH_DEINIT */
+
+const struct bt_mesh_model_cb bt_mesh_light_lightness_srv_cb = {
+ .init = light_lightness_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_lightness_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_lightness_setup_srv_cb = {
+ .init = light_lightness_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_lightness_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_ctl_srv_cb = {
+ .init = light_ctl_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_ctl_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_ctl_setup_srv_cb = {
+ .init = light_ctl_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_ctl_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_ctl_temp_srv_cb = {
+ .init = light_ctl_temp_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_ctl_temp_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_hsl_srv_cb = {
+ .init = light_hsl_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_hsl_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_hsl_setup_srv_cb = {
+ .init = light_hsl_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_hsl_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_hsl_hue_srv_cb = {
+ .init = light_hsl_hue_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_hsl_hue_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_hsl_sat_srv_cb = {
+ .init = light_hsl_sat_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_hsl_sat_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_xyl_srv_cb = {
+ .init = light_xyl_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_xyl_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_xyl_setup_srv_cb = {
+ .init = light_xyl_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_xyl_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_lc_srv_cb = {
+ .init = light_lc_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_lc_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_light_lc_setup_srv_cb = {
+ .init = light_lc_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = light_lc_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+#endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */
diff --git a/lib/bt/esp_ble_mesh/models/server/sensor_server.c b/lib/bt/esp_ble_mesh/models/server/sensor_server.c
new file mode 100644
index 00000000..b920283e
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/sensor_server.c
@@ -0,0 +1,1173 @@
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+
+#include "btc_ble_mesh_sensor_model.h"
+
+#include "mesh/config.h"
+#include "access.h"
+#include "transport.h"
+#include "mesh/model_opcode.h"
+#include "mesh/state_transition.h"
+#include "mesh/device_property.h"
+
+#if CONFIG_BLE_MESH_SENSOR_SERVER
+
+static void update_sensor_periodic_pub(struct bt_mesh_model *model, uint16_t prop_id);
+
+/* message handlers (Start) */
+
+/* Sensor Server & Sensor Setup Server message handlers */
+static void send_sensor_descriptor_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t prop_id, bool get_all)
+{
+ struct bt_mesh_sensor_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t total_len = 5U;
+ int i;
+
+ msg = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN));
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS);
+
+ if (get_all == true) {
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID) {
+ total_len += SENSOR_DESCRIPTOR_LEN;
+ if (total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large sensor descriptor status");
+ break;
+ }
+
+ net_buf_simple_add_le16(msg, state->sensor_property_id);
+ net_buf_simple_add_le32(msg, (state->descriptor.sample_function << 24) |
+ (state->descriptor.negative_tolerance << 12) |
+ (state->descriptor.positive_tolerance));
+ net_buf_simple_add_u8(msg, state->descriptor.measure_period);
+ net_buf_simple_add_u8(msg, state->descriptor.update_interval);
+ }
+ }
+ } else {
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id) {
+ total_len += SENSOR_DESCRIPTOR_LEN;
+ if (total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large sensor descriptor status");
+ break;
+ }
+ net_buf_simple_add_le16(msg, state->sensor_property_id);
+ net_buf_simple_add_le32(msg, (state->descriptor.sample_function << 24) |
+ (state->descriptor.negative_tolerance << 12) |
+ (state->descriptor.positive_tolerance));
+ net_buf_simple_add_u8(msg, state->descriptor.measure_period);
+ net_buf_simple_add_u8(msg, state->descriptor.update_interval);
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_WARN("Sensor Property ID 0x%04x not exists", prop_id);
+ net_buf_simple_add_le16(msg, prop_id);
+ }
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+
+ bt_mesh_free_buf(msg);
+}
+
+static void send_sensor_data_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t prop_id, bool get_all)
+{
+ struct bt_mesh_sensor_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t total_len = 5U;
+ int i;
+
+ msg = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN));
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_STATUS);
+
+ if (get_all == true) {
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID) {
+ uint8_t mpid_len = (state->sensor_data.format == SENSOR_DATA_FORMAT_A) ?
+ SENSOR_DATA_FORMAT_A_MPID_LEN : SENSOR_DATA_FORMAT_B_MPID_LEN;
+ total_len += (mpid_len + (state->sensor_data.raw_value ?
+ state->sensor_data.raw_value->len : 0));
+ if (total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large sensor status");
+ break;
+ }
+
+ if (state->sensor_data.format == SENSOR_DATA_FORMAT_A) {
+ uint16_t mpid = ((state->sensor_property_id & BIT_MASK(11)) << 5) |
+ ((state->sensor_data.length & BIT_MASK(4)) << 1) |
+ state->sensor_data.format;
+ net_buf_simple_add_le16(msg, mpid);
+ } else if (state->sensor_data.format == SENSOR_DATA_FORMAT_B) {
+ uint8_t mpid = (state->sensor_data.length << 1) | state->sensor_data.format;
+ net_buf_simple_add_u8(msg, mpid);
+ net_buf_simple_add_le16(msg, state->sensor_property_id);
+ }
+ if (state->sensor_data.raw_value) {
+ net_buf_simple_add_mem(msg, state->sensor_data.raw_value->data, state->sensor_data.raw_value->len);
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id) {
+ uint8_t mpid_len = (state->sensor_data.format == SENSOR_DATA_FORMAT_A) ?
+ SENSOR_DATA_FORMAT_A_MPID_LEN : SENSOR_DATA_FORMAT_B_MPID_LEN;
+ total_len += (mpid_len + (state->sensor_data.raw_value ?
+ state->sensor_data.raw_value->len : 0));
+ if (total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large sensor status");
+ break;
+ }
+ if (state->sensor_data.format == SENSOR_DATA_FORMAT_A) {
+ uint16_t mpid = ((state->sensor_property_id & BIT_MASK(11)) << 5) |
+ ((state->sensor_data.length & BIT_MASK(4)) << 1) |
+ state->sensor_data.format;
+ net_buf_simple_add_le16(msg, mpid);
+ } else if (state->sensor_data.format == SENSOR_DATA_FORMAT_B) {
+ uint8_t mpid = (state->sensor_data.length << 1) | state->sensor_data.format;
+ net_buf_simple_add_u8(msg, mpid);
+ net_buf_simple_add_le16(msg, state->sensor_property_id);
+ }
+ if (state->sensor_data.raw_value) {
+ net_buf_simple_add_mem(msg, state->sensor_data.raw_value->data,
+ state->sensor_data.raw_value->len);
+ }
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_WARN("Sensor Property ID 0x%04x not exists", prop_id);
+ uint8_t mpid = (SENSOR_DATA_ZERO_LEN << 1) | SENSOR_DATA_FORMAT_B;
+ net_buf_simple_add_u8(msg, mpid);
+ net_buf_simple_add_le16(msg, prop_id);
+ }
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+
+ bt_mesh_free_buf(msg);
+}
+
+static void send_sensor_cadence_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t prop_id, bool publish)
+{
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t length = 0U;
+ int i;
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id && state->cadence) {
+ length = SENSOR_PROPERTY_ID_LEN + 1 + 1;
+ if (state->cadence->trigger_delta_down) {
+ if (state->cadence->trigger_type == SENSOR_STATUS_TRIGGER_TYPE_CHAR) {
+ length += state->cadence->trigger_delta_down->len;
+ } else {
+ length += SENSOR_STATUS_TRIGGER_UINT16_LEN;
+ }
+ }
+ if (state->cadence->trigger_delta_up) {
+ if (state->cadence->trigger_type == SENSOR_STATUS_TRIGGER_TYPE_CHAR) {
+ length += state->cadence->trigger_delta_up->len;
+ } else {
+ length += SENSOR_STATUS_TRIGGER_UINT16_LEN;
+ }
+ }
+ if (state->cadence->fast_cadence_low) {
+ length += state->cadence->fast_cadence_low->len;
+ }
+ if (state->cadence->fast_cadence_high) {
+ length += state->cadence->fast_cadence_high->len;
+ }
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_WARN("Sensor Property ID 0x%04x not exists", prop_id);
+ length = SENSOR_PROPERTY_ID_LEN;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, 1 + length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS);
+ net_buf_simple_add_le16(msg, prop_id);
+ if (i != srv->state_count) {
+ if (state->cadence) {
+ net_buf_simple_add_u8(msg, (state->cadence->trigger_type << 7) |
+ state->cadence->period_divisor);
+ if (state->cadence->trigger_delta_down) {
+ if (state->cadence->trigger_type == SENSOR_STATUS_TRIGGER_TYPE_CHAR) {
+ net_buf_simple_add_mem(msg, state->cadence->trigger_delta_down->data,
+ state->cadence->trigger_delta_down->len);
+ } else {
+ net_buf_simple_add_mem(msg, state->cadence->trigger_delta_down->data,
+ SENSOR_STATUS_TRIGGER_UINT16_LEN);
+ }
+ }
+ if (state->cadence->trigger_delta_up) {
+ if (state->cadence->trigger_type == SENSOR_STATUS_TRIGGER_TYPE_CHAR) {
+ net_buf_simple_add_mem(msg, state->cadence->trigger_delta_up->data,
+ state->cadence->trigger_delta_up->len);
+ } else {
+ net_buf_simple_add_mem(msg, state->cadence->trigger_delta_up->data,
+ SENSOR_STATUS_TRIGGER_UINT16_LEN);
+ }
+ }
+ net_buf_simple_add_u8(msg, state->cadence->min_interval);
+ if (state->cadence->fast_cadence_low) {
+ net_buf_simple_add_mem(msg, state->cadence->fast_cadence_low->data,
+ state->cadence->fast_cadence_low->len);
+ }
+ if (state->cadence->fast_cadence_high) {
+ net_buf_simple_add_mem(msg, state->cadence->fast_cadence_high->data,
+ state->cadence->fast_cadence_high->len);
+ }
+ }
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void send_sensor_settings_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint16_t prop_id)
+{
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct sensor_setting *item = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t total_len = 7U;
+ int i, j;
+
+ msg = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN));
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS);
+ net_buf_simple_add_le16(msg, prop_id);
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id &&
+ state->setting_count && state->settings) {
+ for (j = 0; j < state->setting_count; j++) {
+ item = &state->settings[j];
+ if (item->property_id != INVALID_SENSOR_SETTING_PROPERTY_ID) {
+ total_len += SENSOR_SETTING_PROPERTY_ID_LEN;
+ if (total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large sensor settings status");
+ break;
+ }
+ net_buf_simple_add_le16(msg, item->property_id);
+ }
+ }
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_WARN("Sensor Property ID 0x%04x not exists", prop_id);
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+
+ bt_mesh_free_buf(msg);
+}
+
+static struct sensor_setting *find_sensor_setting(struct bt_mesh_model *model,
+ uint16_t prop_id, uint16_t set_prop_id)
+{
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct sensor_setting *item = NULL;
+ int i, j;
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id &&
+ state->setting_count && state->settings) {
+ for (j = 0; j < state->setting_count; j++) {
+ item = &state->settings[j];
+ if (item->property_id != INVALID_SENSOR_SETTING_PROPERTY_ID &&
+ item->property_id == set_prop_id) {
+ return item;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void send_sensor_setting_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx, uint16_t prop_id,
+ uint16_t set_prop_id, bool publish)
+{
+ struct sensor_setting *item = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t length = 0U;
+
+ item = find_sensor_setting(model, prop_id, set_prop_id);
+ if (item) {
+ length = SENSOR_PROPERTY_ID_LEN + SENSOR_SETTING_PROPERTY_ID_LEN +
+ SENSOR_SETTING_ACCESS_LEN + (item->raw ? item->raw->len : 0);
+ } else {
+ /* If the message is sent as a response to the Sensor Setting Get message or
+ * a Sensor Setting Set message with an unknown Sensor Property ID field or
+ * an unknown Sensor Setting Property ID field, the Sensor Setting Access
+ * field and the Sensor Setting Raw field shall be omitted.
+ */
+ BT_WARN("Sensor Setting not found, 0x%04x, 0x%04x", prop_id, set_prop_id);
+ length = SENSOR_PROPERTY_ID_LEN + SENSOR_SETTING_PROPERTY_ID_LEN;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, 1 + length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS);
+ net_buf_simple_add_le16(msg, prop_id);
+ net_buf_simple_add_le16(msg, set_prop_id);
+ if (item) {
+ /**
+ * If the message is sent as a response to the Sensor Setting Set message with
+ * a Sensor Setting Property ID field that identifies an existing Sensor Setting,
+ * and the value of the Sensor Setting Access state is 0x01 (can be read), the
+ * Sensor Setting Property ID field shall be set to the value of the Sensor
+ * Setting Property ID field of the incoming message, the Sensor Setting Access
+ * field shall be set to the value of the Sensor Setting Access state field, and
+ * the Sensor Setting Raw field shall be omitted.
+ *
+ * TODO: What if the Sensor Setting Access is Prohibited?
+ */
+ net_buf_simple_add_u8(msg, item->access);
+ if (ctx->recv_op != BLE_MESH_MODEL_OP_SENSOR_SETTING_SET ||
+ item->access == SENSOR_SETTING_ACCESS_READ_WRITE) {
+ if (item->raw) {
+ net_buf_simple_add_mem(msg, item->raw->data, item->raw->len);
+ }
+ }
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void send_sensor_column_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf, uint16_t prop_id)
+{
+ struct bt_mesh_sensor_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct net_buf_simple *msg = NULL;
+ bool optional = false;
+ uint16_t length = 0U;
+ int i;
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id) {
+ length = SENSOR_PROPERTY_ID_LEN;
+ if (state->series_column.raw_value_x) {
+ length += state->series_column.raw_value_x->len;
+ }
+ /**
+ * TODO: column width & raw value y in Sensor Column Status are optional,
+ * here we need to add some conditions to decide whether put these two
+ * in the status message.
+ */
+ if (optional) {
+ if (state->series_column.column_width) {
+ length += state->series_column.column_width->len;
+ }
+ if (state->series_column.raw_value_y) {
+ length += state->series_column.raw_value_y->len;
+ }
+ }
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_WARN("Sensor Property ID 0x%04x not exists", prop_id);
+ length = SENSOR_PROPERTY_ID_LEN;
+ }
+
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+
+ /**
+ * TODO: Sensor Column Get contains Raw Value X which identifies a column,
+ * we need to use this value to decide the column.
+ */
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS);
+ net_buf_simple_add_le16(msg, prop_id);
+ if (i != srv->state_count) {
+ if (state->series_column.raw_value_x) {
+ net_buf_simple_add_mem(msg, state->series_column.raw_value_x->data,
+ state->series_column.raw_value_x->len);
+ }
+ if (optional) {
+ if (state->series_column.column_width) {
+ net_buf_simple_add_mem(msg, state->series_column.column_width->data,
+ state->series_column.column_width->len);
+ }
+ if (state->series_column.raw_value_y) {
+ net_buf_simple_add_mem(msg, state->series_column.raw_value_y->data,
+ state->series_column.raw_value_y->len);
+ }
+ }
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+
+ bt_mesh_free_buf(msg);
+}
+
+static void send_sensor_series_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf, uint16_t prop_id)
+{
+ struct bt_mesh_sensor_srv *srv = model->user_data;
+ struct bt_mesh_sensor_state *state = NULL;
+ struct net_buf_simple *msg = NULL;
+ bool optional = false;
+ uint16_t length = 0U;
+ int i;
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id) {
+ length = SENSOR_PROPERTY_ID_LEN;
+ /* TODO: raw value x, column width & raw value y in Sensor Series
+ * Status are optional, here we need to add some conditions to
+ * decide whether put these three in the status message.
+ */
+ if (optional) {
+ if (state->series_column.raw_value_x) {
+ length += state->series_column.raw_value_x->len;
+ }
+ if (state->series_column.column_width) {
+ length += state->series_column.column_width->len;
+ }
+ if (state->series_column.raw_value_y) {
+ length += state->series_column.raw_value_y->len;
+ }
+ }
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_WARN("Sensor Property ID 0x%04x not exists", prop_id);
+ length = SENSOR_PROPERTY_ID_LEN;
+ }
+
+ msg = bt_mesh_alloc_buf(1 + length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+
+ /**
+ * TODO: Sensor Series Get may contain Raw Value X1 and Raw Value X2 which
+ * identifies a starting column and a ending column, we need to use these
+ * values to decide the columns.
+ */
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS);
+ net_buf_simple_add_le16(msg, prop_id);
+ if (i != srv->state_count) {
+ if (optional) {
+ if (state->series_column.raw_value_x) {
+ net_buf_simple_add_mem(msg, state->series_column.raw_value_x->data,
+ state->series_column.raw_value_x->len);
+ }
+ if (state->series_column.column_width) {
+ net_buf_simple_add_mem(msg, state->series_column.column_width->data,
+ state->series_column.column_width->len);
+ }
+ if (state->series_column.raw_value_y) {
+ net_buf_simple_add_mem(msg, state->series_column.raw_value_y->data,
+ state->series_column.raw_value_y->len);
+ }
+ }
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+
+ bt_mesh_free_buf(msg);
+}
+
+static void sensor_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ uint16_t set_prop_id = INVALID_SENSOR_PROPERTY_ID;
+ uint16_t prop_id = INVALID_SENSOR_PROPERTY_ID;
+
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET:
+ case BLE_MESH_MODEL_OP_SENSOR_GET:
+ case BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET:
+ case BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: {
+ struct bt_mesh_sensor_srv *srv = model->user_data;
+ if (srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("Invalid Sensor Server state");
+ return;
+ }
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET ||
+ ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_GET) {
+ bool get_all = buf->len ? false : true;
+ if (buf->len) {
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Property ID 0x0000");
+ return;
+ }
+ }
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET) {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_descriptor_get.op_en = !get_all,
+ .sensor_descriptor_get.id = prop_id,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_descriptor_status(model, ctx, prop_id, get_all);
+ }
+ } else {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_get.op_en = !get_all,
+ .sensor_get.id = prop_id,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_data_status(model, ctx, prop_id, get_all);
+ }
+ }
+ } else {
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Property ID 0x0000");
+ return;
+ }
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET) {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_column_get.id = prop_id,
+ .sensor_column_get.raw_x = buf,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_column_status(model, ctx, buf, prop_id);
+ }
+ } else {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_series_get.id = prop_id,
+ .sensor_series_get.raw = buf,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_series_status(model, ctx, buf, prop_id);
+ }
+ }
+ }
+ return;
+ }
+ case BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET:
+ case BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET:
+ case BLE_MESH_MODEL_OP_SENSOR_SETTING_GET: {
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ if (srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("Invalid Sensor Setup Server state");
+ return;
+ }
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET ||
+ ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET) {
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Property ID 0x0000");
+ return;
+ }
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET) {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_cadence_get.id = prop_id,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_cadence_status(model, ctx, prop_id, false);
+ }
+ } else {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_settings_get.id = prop_id,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_settings_status(model, ctx, prop_id);
+ }
+ }
+ } else {
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Property ID 0x0000");
+ return;
+ }
+ set_prop_id = net_buf_simple_pull_le16(buf);
+ if (set_prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Setting Property ID 0x0000");
+ return;
+ }
+
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_get_msg_t get = {
+ .sensor_setting_get.id = prop_id,
+ .sensor_setting_get.setting_id = set_prop_id,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ } else {
+ send_sensor_setting_status(model, ctx, prop_id, set_prop_id, false);
+ }
+ }
+ return;
+ }
+ default:
+ BT_WARN("Unknown Sensor Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+}
+
+static void sensor_cadence_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ bt_mesh_sensor_server_state_change_t change = {0};
+ struct bt_mesh_sensor_state *state = NULL;
+ struct bt_mesh_model *sensor_model = NULL;
+ struct bt_mesh_elem *element = NULL;
+ uint16_t prop_id = 0U, trigger_len = 0U;
+ uint8_t val = 0U, divisor = 0U;
+ int i;
+
+ if (srv == NULL || srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Property ID 0x0000");
+ return;
+ }
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_set_msg_t set = {
+ .sensor_cadence_set.id = prop_id,
+ .sensor_cadence_set.cadence = buf,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id) {
+ break;
+ }
+ }
+ if (i == srv->state_count || state->cadence == NULL) {
+ /* When the message is sent as a response to the Sensor Cadence Get message or
+ * a Sensor Cadence Set message with an unknown Property ID field or the Sensor
+ * Server does not support the Sensor Cadence state for the sensor referred by
+ * the Property ID, the following fields shall be omitted:
+ * • Fast Cadence Period Divisor
+ * • Status Trigger Type
+ * • Status Trigger Delta Down
+ * • Status Trigger Delta Up
+ * • Status Min Interval
+ * • Fast Cadence Low
+ * • Fast Cadence High
+ */
+ send_sensor_cadence_status(model, ctx, prop_id, false);
+ return;
+ }
+
+ val = net_buf_simple_pull_u8(buf);
+ divisor = val & BIT_MASK(7);
+ if (divisor > SENSOR_PERIOD_DIVISOR_MAX_VALUE) {
+ BT_ERR("Prohibited Fast Cadence Period Divisor 0x%02x", divisor);
+ return;
+ }
+ state->cadence->period_divisor = divisor;
+ state->cadence->trigger_type = (val >> 7) & BIT_MASK(1);
+
+ if (state->cadence->trigger_type == SENSOR_STATUS_TRIGGER_TYPE_CHAR) {
+ trigger_len = bt_mesh_get_dev_prop_len(prop_id);
+ } else {
+ trigger_len = SENSOR_STATUS_TRIGGER_UINT16_LEN;
+ }
+ if (buf->len < (trigger_len << 1) + SENSOR_STATUS_MIN_INTERVAL_LEN) {
+ BT_ERR("Invalid Sensor Cadence Set length %d, trigger type %d",
+ buf->len + 3, state->cadence->trigger_type);
+ return;
+ }
+
+ if (state->cadence->trigger_delta_down) {
+ net_buf_simple_reset(state->cadence->trigger_delta_down);
+ net_buf_simple_add_mem(state->cadence->trigger_delta_down, buf->data, trigger_len);
+ net_buf_simple_pull_mem(buf, trigger_len);
+ }
+ if (state->cadence->trigger_delta_up) {
+ net_buf_simple_reset(state->cadence->trigger_delta_up);
+ net_buf_simple_add_mem(state->cadence->trigger_delta_up, buf->data, trigger_len);
+ net_buf_simple_pull_mem(buf, trigger_len);
+ }
+
+ /* The valid range for the Status Min Interval is 0–26 and other values are Prohibited. */
+ val = net_buf_simple_pull_u8(buf);
+ if (val > SENSOR_STATUS_MIN_INTERVAL_MAX) {
+ BT_ERR("Invalid Status Min Interval %d", val);
+ return;
+ }
+ state->cadence->min_interval = val;
+
+ if (buf->len % 2) {
+ BT_ERR("Different length of Fast Cadence Low & High, length %d", buf->len);
+ return;
+ }
+ if (buf->len) {
+ uint8_t range_len = buf->len / 2;
+ if (state->cadence->fast_cadence_low) {
+ net_buf_simple_reset(state->cadence->fast_cadence_low);
+ net_buf_simple_add_mem(state->cadence->fast_cadence_low, buf->data, range_len);
+ net_buf_simple_pull_mem(buf, range_len);
+ }
+ if (state->cadence->fast_cadence_high) {
+ net_buf_simple_reset(state->cadence->fast_cadence_high);
+ net_buf_simple_add_mem(state->cadence->fast_cadence_high, buf->data, range_len);
+ net_buf_simple_pull_mem(buf, range_len);
+ }
+ }
+
+ change.sensor_cadence_set.id = prop_id;
+ change.sensor_cadence_set.period_divisor = state->cadence->period_divisor;
+ change.sensor_cadence_set.trigger_type = state->cadence->trigger_type;
+ change.sensor_cadence_set.trigger_delta_down = state->cadence->trigger_delta_down;
+ change.sensor_cadence_set.trigger_delta_up = state->cadence->trigger_delta_up;
+ change.sensor_cadence_set.min_interval = state->cadence->min_interval;
+ change.sensor_cadence_set.fast_cadence_low = state->cadence->fast_cadence_low;
+ change.sensor_cadence_set.fast_cadence_high = state->cadence->fast_cadence_high;
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET) {
+ send_sensor_cadence_status(model, ctx, prop_id, false);
+ }
+ send_sensor_cadence_status(model, ctx, prop_id, true);
+
+ /* Try to find the corresponding Sensor Server Model */
+ element = bt_mesh_model_elem(model);
+ sensor_model = bt_mesh_model_find(element, BLE_MESH_MODEL_ID_SENSOR_SRV);
+ if (sensor_model == NULL) {
+ BT_WARN("Sensor Server model not exists in the element");
+ return;
+ }
+
+ /**
+ * Based on the configured Sensor Cadence state, change Periodic Sensor
+ * status publication mechanism.
+ */
+ update_sensor_periodic_pub(sensor_model, prop_id);
+}
+
+static void update_sensor_periodic_pub(struct bt_mesh_model *model, uint16_t prop_id)
+{
+ struct bt_mesh_sensor_state *state = NULL;
+ struct bt_mesh_sensor_srv *srv = NULL;
+ int i;
+
+ if (model->id != BLE_MESH_MODEL_ID_SENSOR_SRV) {
+ BT_ERR("Invalid Sensor Server model 0x%04x", model->id);
+ return;
+ }
+
+ srv = (struct bt_mesh_sensor_srv *)model->user_data;
+ if (srv == NULL || srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ for (i = 0; i < srv->state_count; i++) {
+ state = &srv->states[i];
+ if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
+ state->sensor_property_id == prop_id) {
+ break;
+ }
+ }
+ if (i == srv->state_count) {
+ BT_ERR("Sensor Property ID 0x%04x not exists", prop_id);
+ return;
+ }
+
+ if (state->cadence == NULL) {
+ BT_WARN("Sensor Cadence state not exists");
+ return;
+ }
+
+ /**
+ * Currently when the device receives a Sensor Cadence Set message,
+ * a event will be callback to the application layer, and users can
+ * change the Sensor Data publication period in the event. And this
+ * is exactly what we do for the BQB test.
+ */
+}
+
+static void sensor_setting_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ bt_mesh_sensor_server_state_change_t change = {0};
+ struct sensor_setting *item = NULL;
+ uint16_t prop_id = 0U, set_prop_id = 0U;
+
+ if (srv == NULL || srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ prop_id = net_buf_simple_pull_le16(buf);
+ if (prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Property ID 0x0000");
+ return;
+ }
+
+ set_prop_id = net_buf_simple_pull_le16(buf);
+ if (set_prop_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Prohibited Sensor Setting Property ID 0x0000");
+ return;
+ }
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_sensor_server_recv_set_msg_t set = {
+ .sensor_setting_set.id = prop_id,
+ .sensor_setting_set.setting_id = set_prop_id,
+ .sensor_setting_set.raw = buf,
+ };
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ item = find_sensor_setting(model, prop_id, set_prop_id);
+ if (item) {
+ if (item->access == SENSOR_SETTING_ACCESS_READ_WRITE && item->raw) {
+ net_buf_simple_reset(item->raw);
+ net_buf_simple_add_mem(item->raw, buf->data,
+ MIN(buf->len, item->raw->size));
+
+ change.sensor_setting_set.id = prop_id;
+ change.sensor_setting_set.setting_id = set_prop_id;
+ change.sensor_setting_set.value = item->raw;
+ bt_mesh_sensor_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_SENSOR_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ }
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SENSOR_SETTING_SET) {
+ send_sensor_setting_status(model, ctx, prop_id, set_prop_id, false);
+ }
+ if (item) {
+ send_sensor_setting_status(model, ctx, prop_id, set_prop_id, true);
+ }
+}
+
+/* message handlers (End) */
+
+/* Mapping of message handlers for Sensor Server (0x1100) */
+const struct bt_mesh_model_op bt_mesh_sensor_srv_op[] = {
+ { BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET, 0, sensor_get },
+ { BLE_MESH_MODEL_OP_SENSOR_GET, 0, sensor_get },
+ { BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET, 2, sensor_get },
+ { BLE_MESH_MODEL_OP_SENSOR_SERIES_GET, 2, sensor_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Sensor Setup Server (0x1101) */
+const struct bt_mesh_model_op bt_mesh_sensor_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET, 2, sensor_get },
+ { BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET, 4, sensor_cadence_set },
+ { BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK, 4, sensor_cadence_set },
+ { BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET, 2, sensor_get },
+ { BLE_MESH_MODEL_OP_SENSOR_SETTING_GET, 4, sensor_get },
+ { BLE_MESH_MODEL_OP_SENSOR_SETTING_SET, 4, sensor_setting_set },
+ { BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK, 4, sensor_setting_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+static int check_sensor_server_init(struct bt_mesh_sensor_state *state_start,
+ const uint8_t state_count)
+{
+ struct bt_mesh_sensor_state *state = NULL;
+ struct sensor_setting *setting = NULL;
+ int i, j, k;
+
+ for (i = 0; i < state_count; i++) {
+ state = &state_start[i];
+ if (state->sensor_property_id == INVALID_SENSOR_PROPERTY_ID) {
+ BT_ERR("Invalid Sensor Property ID 0x%04x", state->sensor_property_id);
+ return -EINVAL;
+ }
+ /* Check if the same Sensor Property ID exists */
+ for (k = i + 1; k < state_count; k++) {
+ if (state->sensor_property_id == state_start[k].sensor_property_id) {
+ BT_ERR("Same Sensor Property ID 0x%04x exists", state->sensor_property_id);
+ return -EINVAL;
+ }
+ }
+ if (state->setting_count && state->settings) {
+ for (j = 0; j < state->setting_count; j++) {
+ setting = &state->settings[j];
+ if (setting->property_id == INVALID_SENSOR_SETTING_PROPERTY_ID || setting->raw == NULL) {
+ BT_ERR("Invalid Sensor Setting state");
+ return -EINVAL;
+ }
+ /* Check if the same Sensor Setting Property ID exists */
+ for (k = j + 1; k < state->setting_count; k++) {
+ if (setting->property_id == state->settings[k].property_id) {
+ BT_ERR("Same Sensor Setting Property ID 0x%04x exists", setting->property_id);
+ return -EINVAL;
+ }
+ }
+ }
+ }
+ if (state->cadence) {
+ if (state->cadence->trigger_delta_down == NULL ||
+ state->cadence->trigger_delta_up == NULL ||
+ state->cadence->fast_cadence_low == NULL ||
+ state->cadence->fast_cadence_high == NULL) {
+ BT_ERR("Invalid Sensor Cadence state");
+ return -EINVAL;
+ }
+ }
+ if (state->sensor_data.raw_value == NULL) {
+ BT_ERR("Invalid Sensor Data state");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int sensor_server_init(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Sensor Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_SENSOR_SRV: {
+ struct bt_mesh_sensor_srv *srv = model->user_data;
+ if (srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("Invalid Sensor state, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ if (check_sensor_server_init(srv->states, srv->state_count)) {
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV: {
+ struct bt_mesh_sensor_setup_srv *srv = model->user_data;
+ if (srv->state_count == 0U || srv->states == NULL) {
+ BT_ERR("Invalid Sensor state, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ if (check_sensor_server_init(srv->states, srv->state_count)) {
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ default:
+ BT_WARN("Unknown Sensor Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sensor_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Sensor Server has no publication support");
+ return -EINVAL;
+ }
+
+ /* When this model is present on an element, the corresponding Sensor Setup
+ * Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV) == NULL) {
+ BT_WARN("Sensor Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return sensor_server_init(model);
+}
+
+static int sensor_setup_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Sensor Setup Server has no publication support");
+ return -EINVAL;
+ }
+
+ return sensor_server_init(model);
+}
+
+#if CONFIG_BLE_MESH_DEINIT
+static int sensor_server_deinit(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Sensor Server user, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sensor_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Sensor Server has no publication support");
+ return -EINVAL;
+ }
+
+ return sensor_server_deinit(model);
+}
+
+static int sensor_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Sensor Setup Server has no publication support");
+ return -EINVAL;
+ }
+
+ return sensor_server_deinit(model);
+}
+#endif /* CONFIG_BLE_MESH_DEINIT */
+
+const struct bt_mesh_model_cb bt_mesh_sensor_srv_cb = {
+ .init = sensor_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = sensor_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_sensor_setup_srv_cb = {
+ .init = sensor_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = sensor_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+#endif /* CONFIG_BLE_MESH_SENSOR_SERVER */
diff --git a/lib/bt/esp_ble_mesh/models/server/server_common.c b/lib/bt/esp_ble_mesh/models/server/server_common.c
new file mode 100644
index 00000000..a12ce6ba
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/server_common.c
@@ -0,0 +1,248 @@
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+
+#include "mesh.h"
+#include "mesh/config.h"
+#include "access.h"
+#include "mesh/common.h"
+#include "mesh/generic_server.h"
+#include "mesh/lighting_server.h"
+
+#if CONFIG_BLE_MESH_SERVER_MODEL
+
+/**
+ * According to Mesh Model Spec:
+ * If the Transition Time field is not present and the Generic Default Transition
+ * Time state is supported, the Generic Default Transition Time state shall be
+ * used. Otherwise the transition shall be instantaneous.
+ */
+#define INSTANTANEOUS_TRANS_TIME 0
+
+uint8_t bt_mesh_get_default_trans_time(struct bt_mesh_model *model)
+{
+ /**
+ * 1. If a Generic Default Transition Time Server model is present on the
+ * main element of the model, that model instance shall be used.
+ * 2. If a Generic Default Transition Time Server model is not present on
+ * the main element of the model, then the Generic Default Transition
+ * Time Server model instance that is present on the element with the
+ * largest address that is smaller than the address of the main element
+ * of the node shall be used; if no model instance is present on any
+ * element with an address smaller than the address of the main element,
+ * then the Generic Default Transition Time Server is not supported.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ struct bt_mesh_gen_def_trans_time_srv *state = NULL;
+ uint16_t primary_addr = bt_mesh_primary_addr();
+ struct bt_mesh_model *srv = NULL;
+
+ for (uint16_t addr = element->addr; addr >= primary_addr; addr--) {
+ element = bt_mesh_elem_find(addr);
+ if (element) {
+ srv = bt_mesh_model_find(element, BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV);
+ if (srv) {
+ state = (struct bt_mesh_gen_def_trans_time_srv *)srv->user_data;
+ if (state) {
+ return state->state.trans_time;
+ }
+ }
+ }
+ }
+
+ return INSTANTANEOUS_TRANS_TIME;
+}
+
+int bt_mesh_get_light_lc_trans_time(struct bt_mesh_model *model, uint8_t *trans_time)
+{
+ struct bt_mesh_light_lc_srv *srv = NULL;
+ uint32_t value = 0U;
+
+ if (model == NULL || trans_time == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return -EINVAL;
+ }
+
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_LC_SRV) {
+ BT_ERR("Invalid a Light LC Server 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ srv = (struct bt_mesh_light_lc_srv *)model->user_data;
+ if (srv == NULL) {
+ BT_ERR("Invalid Light LC Server user data");
+ return -EINVAL;
+ }
+
+ /**
+ * 1. Set transition time to 0x54 for BQB test case MESH/SR/LLC/BV-04-C.
+ * Light LC Property Set: 0x3C, 0x004E20 -> Light LC Time Run On
+ * Light LC Property Set: 0x37, 0x004E20 -> Light LC Time Fade On
+ * Light LC Property Set: 0x39, 0x004E20 -> Light LC Time Fade Standby Manual
+ *
+ * 2. Set transition time to 0x0 for BQB test case MESH/SR/LLC/BV-08-C.
+ *
+ * TODO: Based on Light LC state and choose property property value as the
+ * transition time. Currently directly use Light LC Time Run On property value.
+ * Unit: Millisecond, range: [0, 16777214(0xFFFFFE)]
+ */
+ value = srv->lc->prop_state.time_run_on & 0xFFFFFF;
+
+ /**
+ * Convert value into Default Transition Time state format.
+ * 0b00: 0 ~ 6.2s, 100 millisecond step resolution
+ * 0b01: 0 ~ 62s, 1 second step resolution
+ * 0b10: 0 ~ 620s, 10 seconds step resolution
+ * 0b11: 0 ~ 620m, 10 minutes step resolution
+ */
+ if (value <= 6200) {
+ *trans_time = (0 << 6) | (value / 100);
+ } else if (value <= 62000) {
+ *trans_time = (1 << 6) | (value / 1000);
+ } else if (value <= 620000) {
+ *trans_time = (2 << 6) | (value / 10000);
+ } else {
+ *trans_time = (3 << 6) | (value / 600000);
+ }
+
+ return 0;
+}
+
+int bt_mesh_server_get_optional(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf,
+ uint8_t *trans_time, uint8_t *delay,
+ bool *optional)
+{
+ if (model == NULL || buf == NULL || trans_time == NULL ||
+ delay == NULL || optional == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return -EINVAL;
+ }
+
+ if (buf->len != 0x00 && buf->len != 0x02) {
+ BT_ERR("Invalid optional message length %d", buf->len);
+ return -EINVAL;
+ }
+
+ /* No optional fields are available */
+ if (buf->len == 0x00) {
+ if (model->id == BLE_MESH_MODEL_ID_LIGHT_LC_SRV) {
+ /**
+ * Both messages(i.e. Light LC OnOff Set/Set Unack) may optionally include
+ * a Transition Time field indicating the transition time to the target state.
+ * If the Transition Time is not included, the Light LC Server shall use
+ * its appropriate transition times defined by the Light LC Property states.
+ */
+ if (bt_mesh_get_light_lc_trans_time(model, trans_time)) {
+ BT_ERR("Failed to get Light LC transition time");
+ return -EIO;
+ }
+ } else {
+ *trans_time = bt_mesh_get_default_trans_time(model);
+ }
+ *delay = 0U;
+ *optional = false;
+ return 0;
+ }
+
+ /* Optional fields are available */
+ *trans_time = net_buf_simple_pull_u8(buf);
+ if ((*trans_time & 0x3F) == 0x3F) {
+ BT_ERR("Invalid Transaction Number of Steps 0x3f");
+ return -EINVAL;
+ }
+
+ *delay = net_buf_simple_pull_u8(buf);
+ *optional = true;
+ return 0;
+}
+
+void bt_mesh_server_alloc_ctx(struct k_work *work)
+{
+ /**
+ * This function is used to allocate memory for storing "struct bt_mesh_msg_ctx"
+ * of the received messages, because some server models will callback the "struct
+ * bt_mesh_msg_ctx" info to the application layer after a certain delay.
+ * Here we use the allocated heap memory to store the "struct bt_mesh_msg_ctx".
+ */
+ __ASSERT(work, "Invalid parameter");
+ if (!work->user_data) {
+ work->user_data = bt_mesh_calloc(sizeof(struct bt_mesh_msg_ctx));
+ __ASSERT(work->user_data, "Out of memory");
+ }
+}
+
+#if CONFIG_BLE_MESH_DEINIT
+void bt_mesh_server_free_ctx(struct k_work *work)
+{
+ __ASSERT(work, "Invalid parameter");
+ if (work->user_data) {
+ bt_mesh_free(work->user_data);
+ work->user_data = NULL;
+ }
+}
+#endif /* CONFIG_BLE_MESH_DEINIT */
+
+bool bt_mesh_is_server_recv_last_msg(struct bt_mesh_last_msg_info *last,
+ uint8_t tid, uint16_t src, uint16_t dst, int64_t *now)
+{
+ *now = k_uptime_get();
+
+ /* Currently we only compare msg info which dst is set to a unicast address */
+ if (!BLE_MESH_ADDR_IS_UNICAST(dst)) {
+ return false;
+ }
+
+ if (last->tid == tid && last->src == src && last->dst == dst &&
+ (*now - last->timestamp <= K_SECONDS(6))) {
+ return true;
+ }
+
+ return false;
+}
+
+void bt_mesh_server_update_last_msg(struct bt_mesh_last_msg_info *last,
+ uint8_t tid, uint16_t src, uint16_t dst, int64_t *now)
+{
+ /* Currently we only update msg info which dst is set to a unicast address */
+ if (!BLE_MESH_ADDR_IS_UNICAST(dst)) {
+ return;
+ }
+
+ last->tid = tid;
+ last->src = src;
+ last->dst = dst;
+ last->timestamp = *now;
+}
+
+struct net_buf_simple *bt_mesh_server_get_pub_msg(struct bt_mesh_model *model, uint16_t msg_len)
+{
+ struct net_buf_simple *buf = NULL;
+
+ if (model == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return NULL;
+ }
+
+ if (model->pub == NULL || model->pub->msg == NULL ||
+ model->pub->addr == BLE_MESH_ADDR_UNASSIGNED) {
+ BT_DBG("No publication support, model id 0x%04x", model->id);
+ return NULL;
+ }
+
+ buf = model->pub->msg;
+ if (buf->size < msg_len) {
+ BT_ERR("Too small publication msg size %d, model id 0x%04x",
+ buf->size, model->id);
+ return NULL;
+ }
+
+ return buf;
+}
+
+#endif /* CONFIG_BLE_MESH_SERVER_MODEL */
diff --git a/lib/bt/esp_ble_mesh/models/server/state_binding.c b/lib/bt/esp_ble_mesh/models/server/state_binding.c
new file mode 100644
index 00000000..4a2da3aa
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/state_binding.c
@@ -0,0 +1,336 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+
+#include "mesh/config.h"
+#include "mesh/common.h"
+#include "mesh/model_common.h"
+#include "mesh/model_opcode.h"
+#include "mesh/state_binding.h"
+#include "mesh/state_transition.h"
+
+#if CONFIG_BLE_MESH_SERVER_MODEL
+
+uint16_t bt_mesh_convert_lightness_actual_to_linear(uint16_t actual)
+{
+ float tmp = ((float) actual / UINT16_MAX);
+
+ return bt_mesh_ceil(UINT16_MAX * tmp * tmp);
+}
+
+uint16_t bt_mesh_convert_lightness_linear_to_actual(uint16_t linear)
+{
+ return (uint16_t) (UINT16_MAX * bt_mesh_sqrt(((float) linear / UINT16_MAX)));
+}
+
+int16_t bt_mesh_convert_temperature_to_gen_level(uint16_t temp, uint16_t min, uint16_t max)
+{
+ float tmp = (temp - min) * UINT16_MAX / (max - min);
+ return (int16_t) (tmp + INT16_MIN);
+}
+
+uint16_t bt_mesh_covert_gen_level_to_temperature(int16_t level, uint16_t min, uint16_t max)
+{
+ float diff = (float) (max - min) / UINT16_MAX;
+ uint16_t tmp = (uint16_t) ((level - INT16_MIN) * diff);
+ return (uint16_t) (min + tmp);
+}
+
+int16_t bt_mesh_convert_hue_to_level(uint16_t hue)
+{
+ return (int16_t) (hue + INT16_MIN);
+}
+
+uint16_t bt_mesh_convert_level_to_hue(int16_t level)
+{
+ return (uint16_t) (level - INT16_MIN);
+}
+
+int16_t bt_mesh_convert_saturation_to_level(uint16_t saturation)
+{
+ return (int16_t) (saturation + INT16_MIN);
+}
+
+uint16_t bt_mesh_convert_level_to_saturation(int16_t level)
+{
+ return (uint16_t) (level - INT16_MIN);
+}
+
+int bt_mesh_update_binding_state(struct bt_mesh_model *model,
+ bt_mesh_server_state_type_t type,
+ bt_mesh_server_state_value_t *value)
+{
+ if (model == NULL || model->user_data == NULL ||
+ value == NULL || type > BIND_STATE_MAX) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return -EINVAL;
+ }
+
+ switch (type) {
+#if CONFIG_BLE_MESH_GENERIC_SERVER
+ case GENERIC_ONOFF_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_GEN_ONOFF_SRV) {
+ BT_ERR("Invalid Generic OnOff Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_gen_onoff_srv *srv = model->user_data;
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state.onoff = value->gen_onoff.onoff;
+ gen_onoff_publish(model);
+ break;
+ }
+ case GENERIC_LEVEL_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_GEN_LEVEL_SRV) {
+ BT_ERR("Invalid Generic Level Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_gen_level_srv *srv = model->user_data;
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state.level = value->gen_level.level;
+ gen_level_publish(model);
+ break;
+ }
+ case GENERIC_ONPOWERUP_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV) {
+ BT_ERR("Invalid Generic Power OnOff Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_gen_power_onoff_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power OnOff Server state");
+ return -EINVAL;
+ }
+
+ srv->state->onpowerup = value->gen_onpowerup.onpowerup;
+ gen_onpowerup_publish(model);
+ break;
+ }
+ case GENERIC_POWER_ACTUAL_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV) {
+ BT_ERR("Invalid Generic Power Level Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_gen_power_level_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Generic Power Level Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->power_actual = value->gen_power_actual.power;
+ /**
+ * Whenever the Generic Power Actual state is changed to a non-zero value
+ * as a result of a non-transactional message or a completed sequence of
+ * transactional messages, the value of the Generic Power Last state shall
+ * be set to the value of the Generic Power Actual state.
+ */
+ if (srv->state->power_actual) {
+ srv->state->power_last = srv->state->power_actual;
+ }
+ gen_power_level_publish(model, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+ break;
+ }
+#endif /* CONFIG_BLE_MESH_GENERIC_SERVER */
+#if CONFIG_BLE_MESH_LIGHTING_SERVER
+ case LIGHT_LIGHTNESS_ACTUAL_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV) {
+ BT_ERR("Invalid Light Lightness Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->actual_transition);
+ srv->state->lightness_actual = value->light_lightness_actual.lightness;
+ /**
+ * Whenever the Light Lightness Actual state is changed with a non-transactional
+ * message or a completed sequence of transactional messages to a non-zero value,
+ * the value of the Light Lightness Last shall be set to the value of the Light
+ * Lightness Actual.
+ */
+ if (srv->state->lightness_actual) {
+ srv->state->lightness_last = srv->state->lightness_actual;
+ }
+ light_lightness_publish(model, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+ break;
+ }
+ case LIGHT_LIGHTNESS_LINEAR_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV) {
+ BT_ERR("Invalid Light Lightness Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_lightness_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light Lightness Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->linear_transition);
+ srv->state->lightness_linear = value->light_lightness_linear.lightness;
+ light_lightness_publish(model, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+ break;
+ }
+ case LIGHT_CTL_LIGHTNESS_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_CTL_SRV) {
+ BT_ERR("Invalid Light CTL Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_ctl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->lightness = value->light_ctl_lightness.lightness;
+ light_ctl_publish(model, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+ break;
+ }
+ case LIGHT_CTL_TEMP_DELTA_UV_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV) {
+ BT_ERR("Invalid Light CTL Temperature Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_ctl_temp_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light CTL Temperature Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->temperature = value->light_ctl_temp_delta_uv.temperature;
+ srv->state->delta_uv = value->light_ctl_temp_delta_uv.delta_uv;
+ light_ctl_publish(model, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+ break;
+ }
+ case LIGHT_HSL_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_HSL_SRV) {
+ BT_ERR("Invalid Light HSL Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->lightness = value->light_hsl.lightness;
+ srv->state->hue = value->light_hsl.hue;
+ srv->state->saturation = value->light_hsl.saturation;
+ light_hsl_publish(model, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+ break;
+ }
+ case LIGHT_HSL_LIGHTNESS_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_HSL_SRV) {
+ BT_ERR("Invalid Light HSL Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_hsl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->lightness = value->light_hsl_lightness.lightness;
+ light_hsl_publish(model, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+ break;
+ }
+ case LIGHT_HSL_HUE_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV) {
+ BT_ERR("Invalid Light HSL Hue Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_hsl_hue_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Hue Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->hue = value->light_hsl_hue.hue;
+ light_hsl_publish(model, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+ break;
+ }
+ case LIGHT_HSL_SATURATION_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV) {
+ BT_ERR("Invalid Light HSL Saturation Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_hsl_sat_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light HSL Saturation Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->saturation = value->light_hsl_saturation.saturation;
+ light_hsl_publish(model, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+ break;
+ }
+ case LIGHT_XYL_LIGHTNESS_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_XYL_SRV) {
+ BT_ERR("Invalid Light xyL Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_xyl_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Light xyL Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->state->lightness = value->light_xyl_lightness.lightness;
+ light_xyl_publish(model, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+ break;
+ }
+ case LIGHT_LC_LIGHT_ONOFF_STATE: {
+ if (model->id != BLE_MESH_MODEL_ID_LIGHT_LC_SRV) {
+ BT_ERR("Invalid Light LC Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ struct bt_mesh_light_lc_srv *srv = model->user_data;
+ if (srv->lc == NULL) {
+ BT_ERR("Invalid Light LC Server state");
+ return -EINVAL;
+ }
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ srv->lc->state.light_onoff = value->light_lc_light_onoff.onoff;
+ light_lc_publish(model, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+ break;
+ }
+#endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */
+ default:
+ BT_WARN("Unknown binding state type 0x%02x", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_BLE_MESH_SERVER_MODEL */
diff --git a/lib/bt/esp_ble_mesh/models/server/state_transition.c b/lib/bt/esp_ble_mesh/models/server/state_transition.c
new file mode 100644
index 00000000..46c82816
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/state_transition.c
@@ -0,0 +1,1040 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * SPDX-FileCopyrightText: 2018 Vikrant More
+ * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "btc_ble_mesh_generic_model.h"
+#include "btc_ble_mesh_lighting_model.h"
+#include "btc_ble_mesh_time_scene_model.h"
+#include "btc_ble_mesh_sensor_model.h"
+
+#include "mesh/config.h"
+#include "mesh/model_opcode.h"
+#include "mesh/state_transition.h"
+
+#if (CONFIG_BLE_MESH_GENERIC_SERVER || \
+ CONFIG_BLE_MESH_TIME_SCENE_SERVER || \
+ CONFIG_BLE_MESH_LIGHTING_SERVER)
+
+/* Function to calculate Remaining Time (Start) */
+
+void bt_mesh_server_calc_remain_time(struct bt_mesh_state_transition *transition)
+{
+ uint8_t steps = 0U, resolution = 0U;
+ int32_t duration_remainder = 0;
+ int64_t now = 0;
+
+ if (transition->just_started) {
+ transition->remain_time = transition->trans_time;
+ } else {
+ now = k_uptime_get();
+ duration_remainder = transition->total_duration -
+ (now - transition->start_timestamp);
+ if (duration_remainder > 620000) {
+ /* > 620 seconds -> resolution = 0b11 [10 minutes] */
+ resolution = 0x03;
+ steps = duration_remainder / 600000;
+ } else if (duration_remainder > 62000) {
+ /* > 62 seconds -> resolution = 0b10 [10 seconds] */
+ resolution = 0x02;
+ steps = duration_remainder / 10000;
+ } else if (duration_remainder > 6200) {
+ /* > 6.2 seconds -> resolution = 0b01 [1 seconds] */
+ resolution = 0x01;
+ steps = duration_remainder / 1000;
+ } else if (duration_remainder > 0) {
+ /* <= 6.2 seconds -> resolution = 0b00 [100 ms] */
+ resolution = 0x00;
+ steps = duration_remainder / 100;
+ } else {
+ resolution = 0x00;
+ steps = 0x00;
+ }
+
+ transition->remain_time = (resolution << 6) | steps;
+ }
+}
+
+/* Function to calculate Remaining Time (End) */
+
+static void tt_values_calculator(struct bt_mesh_state_transition *transition)
+{
+ uint8_t steps_multiplier = 0U, resolution = 0U;
+
+ resolution = (transition->trans_time >> 6);
+ steps_multiplier = (transition->trans_time & 0x3F);
+
+ switch (resolution) {
+ case 0: /* 100ms */
+ transition->total_duration = steps_multiplier * 100;
+ break;
+ case 1: /* 1 second */
+ transition->total_duration = steps_multiplier * 1000;
+ break;
+ case 2: /* 10 seconds */
+ transition->total_duration = steps_multiplier * 10000;
+ break;
+ case 3: /* 10 minutes */
+ transition->total_duration = steps_multiplier * 600000;
+ break;
+ }
+
+ transition->counter = ((float) transition->total_duration / 100);
+
+ if (transition->counter > BLE_MESH_DEVICE_SPECIFIC_RESOLUTION) {
+ transition->counter = BLE_MESH_DEVICE_SPECIFIC_RESOLUTION;
+ }
+}
+
+static void transition_time_values(struct bt_mesh_state_transition *transition,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition->trans_time = trans_time;
+ transition->delay = delay;
+
+ if (trans_time == 0U) {
+ return;
+ }
+
+ tt_values_calculator(transition);
+ transition->quo_tt = transition->total_duration / transition->counter;
+}
+
+static void transition_timer_start(struct bt_mesh_state_transition *transition)
+{
+ transition->start_timestamp = k_uptime_get();
+ k_delayed_work_submit_periodic(&transition->timer, K_MSEC(transition->quo_tt));
+ bt_mesh_atomic_set_bit(transition->flag, BLE_MESH_TRANS_TIMER_START);
+}
+
+static void transition_timer_stop(struct bt_mesh_state_transition *transition)
+{
+ k_delayed_work_cancel(&transition->timer);
+ bt_mesh_atomic_clear_bit(transition->flag, BLE_MESH_TRANS_TIMER_START);
+}
+
+#if CONFIG_BLE_MESH_GENERIC_SERVER
+void generic_onoff_tt_values(struct bt_mesh_gen_onoff_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ return transition_time_values(&srv->transition, trans_time, delay);
+}
+
+void generic_level_tt_values(struct bt_mesh_gen_level_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_level =
+ ((float) (srv->state.level - srv->state.target_level) / srv->transition.counter);
+}
+
+void generic_power_level_tt_values(struct bt_mesh_gen_power_level_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_level =
+ ((float) (srv->state->power_actual - srv->state->target_power_actual) / srv->transition.counter);
+}
+#endif /* CONFIG_BLE_MESH_GENERIC_SERVER */
+
+#if CONFIG_BLE_MESH_LIGHTING_SERVER
+void light_lightness_actual_tt_values(struct bt_mesh_light_lightness_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->actual_transition, trans_time, delay);
+ srv->tt_delta_lightness_actual =
+ ((float) (srv->state->lightness_actual - srv->state->target_lightness_actual) / srv->actual_transition.counter);
+}
+
+void light_lightness_linear_tt_values(struct bt_mesh_light_lightness_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->linear_transition, trans_time, delay);
+ srv->tt_delta_lightness_linear =
+ ((float) (srv->state->lightness_linear - srv->state->target_lightness_linear) / srv->linear_transition.counter);
+}
+
+void light_ctl_tt_values(struct bt_mesh_light_ctl_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_lightness =
+ ((float) (srv->state->lightness - srv->state->target_lightness) / srv->transition.counter);
+ srv->tt_delta_temperature =
+ ((float) (srv->state->temperature - srv->state->target_temperature) / srv->transition.counter);
+ srv->tt_delta_delta_uv =
+ ((float) (srv->state->delta_uv - srv->state->target_delta_uv) / srv->transition.counter);
+}
+
+void light_ctl_temp_tt_values(struct bt_mesh_light_ctl_temp_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_temperature =
+ ((float) (srv->state->temperature - srv->state->target_temperature) / srv->transition.counter);
+ srv->tt_delta_delta_uv =
+ ((float) (srv->state->delta_uv - srv->state->target_delta_uv) / srv->transition.counter);
+}
+
+void light_hsl_tt_values(struct bt_mesh_light_hsl_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_lightness =
+ ((float) (srv->state->lightness - srv->state->target_lightness) / srv->transition.counter);
+ srv->tt_delta_hue =
+ ((float) (srv->state->hue - srv->state->target_hue) / srv->transition.counter);
+ srv->tt_delta_saturation =
+ ((float) (srv->state->saturation - srv->state->target_saturation) / srv->transition.counter);
+}
+
+void light_hsl_hue_tt_values(struct bt_mesh_light_hsl_hue_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_hue =
+ ((float) (srv->state->hue - srv->state->target_hue) / srv->transition.counter);
+}
+
+void light_hsl_sat_tt_values(struct bt_mesh_light_hsl_sat_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_saturation =
+ ((float) (srv->state->saturation - srv->state->target_saturation) / srv->transition.counter);
+}
+
+void light_xyl_tt_values(struct bt_mesh_light_xyl_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+ srv->tt_delta_lightness =
+ ((float) (srv->state->lightness - srv->state->target_lightness) / srv->transition.counter);
+ srv->tt_delta_x =
+ ((float) (srv->state->x - srv->state->target_x) / srv->transition.counter);
+ srv->tt_delta_y =
+ ((float) (srv->state->y - srv->state->target_y) / srv->transition.counter);
+}
+
+void light_lc_tt_values(struct bt_mesh_light_lc_srv *srv,
+ uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+}
+#endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */
+
+#if CONFIG_BLE_MESH_TIME_SCENE_SERVER
+void scene_tt_values(struct bt_mesh_scene_srv *srv, uint8_t trans_time, uint8_t delay)
+{
+ transition_time_values(&srv->transition, trans_time, delay);
+}
+#endif /* CONFIG_BLE_MESH_TIME_SCENE_SERVER */
+
+/* Timers related handlers & threads (Start) */
+
+#if CONFIG_BLE_MESH_GENERIC_SERVER
+void generic_onoff_work_handler(struct k_work *work)
+{
+ struct bt_mesh_gen_onoff_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_gen_onoff_srv,
+ transition.timer.work);
+ bt_mesh_gen_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.gen_onoff_set.onoff = srv->state.onoff;
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ /**
+ * Because binary states cannot support transitions, when changing to
+ * 0x01 (On), the Generic OnOff state shall change immediately when
+ * the transition starts, and when changing to 0x00, the state shall
+ * change when the transition finishes.
+ */
+ if (srv->state.target_onoff == BLE_MESH_STATE_ON) {
+ srv->state.onoff = BLE_MESH_STATE_ON;
+ change.gen_onoff_set.onoff = srv->state.onoff;
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state.onoff = srv->state.target_onoff;
+ if (srv->state.target_onoff != BLE_MESH_STATE_ON) {
+ change.gen_onoff_set.onoff = srv->state.onoff;
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ }
+
+ gen_onoff_publish(srv->model);
+
+ bt_mesh_generic_server_unlock();
+}
+
+void generic_level_work_handler(struct k_work *work)
+{
+ struct bt_mesh_gen_level_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_gen_level_srv,
+ transition.timer.work);
+ bt_mesh_gen_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_LEVEL_SET:
+ case BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK:
+ change.gen_level_set.level = srv->state.level;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_DELTA_SET:
+ case BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK:
+ change.gen_delta_set.level = srv->state.level;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_MOVE_SET:
+ case BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK:
+ change.gen_move_set.level = srv->state.level;
+ break;
+ }
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state.level -= srv->tt_delta_level;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state.level = srv->state.target_level;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_GEN_LEVEL_SET:
+ case BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK:
+ change.gen_level_set.level = srv->state.level;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_DELTA_SET:
+ case BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK:
+ change.gen_delta_set.level = srv->state.level;
+ break;
+ case BLE_MESH_MODEL_OP_GEN_MOVE_SET:
+ case BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK:
+ change.gen_move_set.level = srv->state.level;
+ break;
+ }
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ gen_level_publish(srv->model);
+
+ bt_mesh_generic_server_unlock();
+}
+
+void generic_power_level_work_handler(struct k_work *work)
+{
+ struct bt_mesh_gen_power_level_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_gen_power_level_srv,
+ transition.timer.work);
+ bt_mesh_gen_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_generic_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.gen_power_level_set.power = srv->state->power_actual;
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_generic_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->power_actual -= srv->tt_delta_level;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+
+ srv->state->power_actual = srv->state->target_power_actual;
+ /**
+ * Whenever the Generic Power Actual state is changed to a non-zero value
+ * as a result of a non-transactional message or a completed sequence of
+ * transactional messages, the value of the Generic Power Last state shall
+ * be set to the value of the Generic Power Actual state.
+ */
+ if (srv->state->power_actual) {
+ srv->state->power_last = srv->state->power_actual;
+ }
+ }
+
+ change.gen_power_level_set.power = srv->state->power_actual;
+ bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ gen_power_level_publish(srv->model, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS);
+
+ bt_mesh_generic_server_unlock();
+}
+#endif /* CONFIG_BLE_MESH_GENERIC_SERVER */
+
+#if CONFIG_BLE_MESH_LIGHTING_SERVER
+void light_lightness_actual_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_lightness_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_lightness_srv,
+ actual_transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->actual_transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->actual_transition.timer.work.user_data;
+
+ if (srv->actual_transition.just_started) {
+ srv->actual_transition.just_started = false;
+ if (srv->actual_transition.counter == 0U) {
+ change.lightness_set.lightness = srv->state->lightness_actual;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->actual_transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->actual_transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->actual_transition.counter != 0U) {
+ srv->actual_transition.counter--;
+ srv->state->lightness_actual -= srv->tt_delta_lightness_actual;
+ }
+
+ if (srv->actual_transition.counter == 0U) {
+ transition_timer_stop(&srv->actual_transition);
+
+ srv->state->lightness_actual = srv->state->target_lightness_actual;
+ /**
+ * Whenever the Light Lightness Actual state is changed with a non-
+ * transactional message or a completed sequence of transactional
+ * messages to a non-zero value, the value of the Light Lightness
+ * Last shall be set to the value of the Light Lightness Actual.
+ */
+ if (srv->state->lightness_actual) {
+ srv->state->lightness_last = srv->state->lightness_actual;
+ }
+ }
+
+ change.lightness_set.lightness = srv->state->lightness_actual;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_lightness_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_lightness_linear_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_lightness_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_lightness_srv,
+ linear_transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->linear_transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->linear_transition.timer.work.user_data;
+
+ if (srv->linear_transition.just_started) {
+ srv->linear_transition.just_started = false;
+ if (srv->linear_transition.counter == 0U) {
+ change.lightness_linear_set.lightness = srv->state->lightness_linear;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->linear_transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->linear_transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->linear_transition.counter != 0U) {
+ srv->linear_transition.counter--;
+ srv->state->lightness_linear -= srv->tt_delta_lightness_linear;
+ }
+
+ if (srv->linear_transition.counter == 0U) {
+ transition_timer_stop(&srv->linear_transition);
+ srv->state->lightness_linear = srv->state->target_lightness_linear;
+ }
+
+ change.lightness_linear_set.lightness = srv->state->lightness_linear;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_lightness_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_ctl_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_ctl_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_ctl_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.ctl_set.lightness = srv->state->lightness;
+ change.ctl_set.temperature = srv->state->temperature;
+ change.ctl_set.delta_uv = srv->state->delta_uv;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->lightness -= srv->tt_delta_lightness;
+ srv->state->temperature -= srv->tt_delta_temperature;
+ srv->state->delta_uv -= srv->tt_delta_delta_uv;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->lightness = srv->state->target_lightness;
+ srv->state->temperature = srv->state->target_temperature;
+ srv->state->delta_uv = srv->state->target_delta_uv;
+ }
+
+ change.ctl_set.lightness = srv->state->lightness;
+ change.ctl_set.temperature = srv->state->temperature;
+ change.ctl_set.delta_uv = srv->state->delta_uv;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_ctl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_ctl_temp_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_ctl_temp_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_ctl_temp_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.ctl_temp_set.temperature = srv->state->temperature;
+ change.ctl_temp_set.delta_uv = srv->state->delta_uv;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->temperature -= srv->tt_delta_temperature;
+ srv->state->delta_uv -= srv->tt_delta_delta_uv;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->temperature = srv->state->target_temperature;
+ srv->state->delta_uv = srv->state->target_delta_uv;
+ }
+
+ change.ctl_temp_set.temperature = srv->state->temperature;
+ change.ctl_temp_set.delta_uv = srv->state->delta_uv;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_ctl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_hsl_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_hsl_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_hsl_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.hsl_set.lightness = srv->state->lightness;
+ change.hsl_set.hue = srv->state->hue;
+ change.hsl_set.saturation = srv->state->saturation;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->lightness -= srv->tt_delta_lightness;
+ srv->state->hue -= srv->tt_delta_hue;
+ srv->state->saturation -= srv->tt_delta_saturation;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->lightness = srv->state->target_lightness;
+ srv->state->hue = srv->state->target_hue;
+ srv->state->saturation = srv->state->target_saturation;
+ }
+
+ change.hsl_set.lightness = srv->state->lightness;
+ change.hsl_set.hue = srv->state->hue;
+ change.hsl_set.saturation = srv->state->saturation;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_hsl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_hsl_hue_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_hsl_hue_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_hsl_hue_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.hsl_hue_set.hue = srv->state->hue;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->hue -= srv->tt_delta_hue;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->hue = srv->state->target_hue;
+ }
+
+ change.hsl_hue_set.hue = srv->state->hue;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_hsl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_hsl_sat_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_hsl_sat_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_hsl_sat_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.hsl_saturation_set.saturation = srv->state->saturation;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->saturation -= srv->tt_delta_saturation;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->saturation = srv->state->target_saturation;
+ }
+
+ change.hsl_saturation_set.saturation = srv->state->saturation;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_hsl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_xyl_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_xyl_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_xyl_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.xyl_set.lightness = srv->state->lightness;
+ change.xyl_set.x = srv->state->x;
+ change.xyl_set.y = srv->state->y;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ srv->state->lightness -= srv->tt_delta_lightness;
+ srv->state->x -= srv->tt_delta_x;
+ srv->state->y -= srv->tt_delta_y;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->lightness = srv->state->target_lightness;
+ srv->state->x = srv->state->target_x;
+ srv->state->y = srv->state->target_y;
+ }
+
+ change.xyl_set.lightness = srv->state->lightness;
+ change.xyl_set.x = srv->state->x;
+ change.xyl_set.y = srv->state->y;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ light_xyl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+
+void light_lc_work_handler(struct k_work *work)
+{
+ struct bt_mesh_light_lc_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_light_lc_srv,
+ transition.timer.work);
+ bt_mesh_light_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_light_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.lc_light_onoff_set.onoff = srv->lc->state.light_onoff;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ /**
+ * Because binary states cannot support transitions, when changing to
+ * 0x01 (On), the Generic OnOff state shall change immediately when
+ * the transition starts, and when changing to 0x00, the state shall
+ * change when the transition finishes.
+ */
+ if (srv->lc->state.target_light_onoff == BLE_MESH_STATE_ON) {
+ srv->lc->state.light_onoff = BLE_MESH_STATE_ON;
+ bt_mesh_light_server_state_change_t change = {
+ .lc_light_onoff_set.onoff = srv->lc->state.light_onoff,
+ };
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_light_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->lc->state.light_onoff = srv->lc->state.target_light_onoff;
+ if (srv->lc->state.light_onoff != BLE_MESH_STATE_ON) {
+ change.lc_light_onoff_set.onoff = srv->lc->state.light_onoff;
+ bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ }
+
+ light_lc_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS);
+
+ bt_mesh_light_server_unlock();
+}
+#endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */
+
+#if CONFIG_BLE_MESH_TIME_SCENE_SERVER
+void scene_recall_work_handler(struct k_work *work)
+{
+ struct bt_mesh_scene_srv *srv = CONTAINER_OF(work,
+ struct bt_mesh_scene_srv,
+ transition.timer.work);
+ bt_mesh_time_scene_server_state_change_t change = {0};
+ struct bt_mesh_msg_ctx *ctx = NULL;
+
+ if (srv == NULL || srv->state == NULL ||
+ srv->transition.timer.work.user_data == NULL) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ bt_mesh_time_scene_server_lock();
+
+ ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data;
+
+ if (srv->transition.just_started) {
+ srv->transition.just_started = false;
+ if (srv->transition.counter == 0U) {
+ change.scene_recall.scene_number = srv->state->current_scene;
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+ bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START);
+ } else {
+ transition_timer_start(&srv->transition);
+ }
+
+ bt_mesh_time_scene_server_unlock();
+ return;
+ }
+
+ if (srv->transition.counter != 0U) {
+ srv->transition.counter--;
+ }
+
+ if (srv->transition.counter == 0U) {
+ transition_timer_stop(&srv->transition);
+ srv->state->current_scene = srv->state->target_scene;
+ srv->state->in_progress = false;
+ srv->state->target_scene = INVALID_SCENE_NUMBER;
+ }
+
+ change.scene_recall.scene_number = srv->state->current_scene;
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ srv->model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ scene_publish(srv->model, ctx, BLE_MESH_MODEL_OP_SCENE_STATUS);
+
+ bt_mesh_time_scene_server_unlock();
+}
+#endif /* CONFIG_BLE_MESH_TIME_SCENE_SERVER */
+
+/* Timers related handlers & threads (End) */
+
+void bt_mesh_server_stop_transition(struct bt_mesh_state_transition *transition)
+{
+ memset(transition, 0x0, offsetof(struct bt_mesh_state_transition, flag));
+ if (bt_mesh_atomic_test_and_clear_bit(transition->flag, BLE_MESH_TRANS_TIMER_START)) {
+ k_delayed_work_cancel(&transition->timer);
+ }
+}
+
+void bt_mesh_server_start_transition(struct bt_mesh_state_transition *transition)
+{
+ k_delayed_work_submit(&transition->timer, K_MSEC(5 * transition->delay));
+ if (transition->delay) {
+ bt_mesh_atomic_set_bit(transition->flag, BLE_MESH_TRANS_TIMER_START);
+ }
+}
+
+/* Messages handlers (End) */
+
+#endif /* (CONFIG_BLE_MESH_GENERIC_SERVER || \
+ CONFIG_BLE_MESH_TIME_SCENE_SERVER || \
+ CONFIG_BLE_MESH_LIGHTING_SERVER) */
diff --git a/lib/bt/esp_ble_mesh/models/server/time_scene_server.c b/lib/bt/esp_ble_mesh/models/server/time_scene_server.c
new file mode 100644
index 00000000..3494653a
--- /dev/null
+++ b/lib/bt/esp_ble_mesh/models/server/time_scene_server.c
@@ -0,0 +1,1536 @@
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+
+#include "btc_ble_mesh_time_scene_model.h"
+
+#include "mesh/config.h"
+#include "access.h"
+#include "transport.h"
+#include "mesh/model_opcode.h"
+#include "mesh/state_transition.h"
+
+#if CONFIG_BLE_MESH_TIME_SCENE_SERVER
+
+static bt_mesh_mutex_t time_scene_server_lock;
+
+void bt_mesh_time_scene_server_lock(void)
+{
+ bt_mesh_mutex_lock(&time_scene_server_lock);
+}
+
+void bt_mesh_time_scene_server_unlock(void)
+{
+ bt_mesh_mutex_unlock(&time_scene_server_lock);
+}
+
+/* message handlers (Start) */
+
+/* Time Server & Time Setup Server message handlers */
+static void send_time_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish, uint16_t opcode)
+{
+ struct net_buf_simple *msg = NULL;
+ uint8_t zero[5] = {0};
+ uint8_t length = 1 + 10;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, opcode);
+ switch (opcode) {
+ case BLE_MESH_MODEL_OP_TIME_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_TIME_SRV) {
+ struct bt_mesh_time_srv *srv = model->user_data;
+ net_buf_simple_add_mem(msg, srv->state->time.tai_seconds, TAI_SECONDS_LEN);
+ if (memcmp(srv->state->time.tai_seconds, zero, TAI_SECONDS_LEN)) {
+ net_buf_simple_add_u8(msg, srv->state->time.subsecond);
+ /**
+ * Set the Uncertainty field to a value that is a sum of the value of
+ * the Uncertainty state and an estimated time it will take the message
+ * to be processed before being sent on the radio interface.
+ *
+ * TODO: how to estimate the processing time?
+ */
+ net_buf_simple_add_u8(msg, srv->state->time.uncertainty);
+ net_buf_simple_add_le16(msg,
+ (srv->state->time.tai_utc_delta_curr << 1) | srv->state->time.time_authority);
+ net_buf_simple_add_u8(msg, srv->state->time.time_zone_offset_curr);
+ }
+ } else if (model->id == BLE_MESH_MODEL_ID_TIME_SETUP_SRV) {
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ net_buf_simple_add_mem(msg, srv->state->time.tai_seconds, TAI_SECONDS_LEN);
+ if (memcmp(srv->state->time.tai_seconds, zero, TAI_SECONDS_LEN)) {
+ net_buf_simple_add_u8(msg, srv->state->time.subsecond);
+ net_buf_simple_add_u8(msg, srv->state->time.uncertainty);
+ net_buf_simple_add_le16(msg,
+ (srv->state->time.tai_utc_delta_curr << 1) | srv->state->time.time_authority);
+ net_buf_simple_add_u8(msg, srv->state->time.time_zone_offset_curr);
+ }
+ }
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ZONE_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_TIME_SRV) {
+ struct bt_mesh_time_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->time.time_zone_offset_curr);
+ net_buf_simple_add_u8(msg, srv->state->time.time_zone_offset_new);
+ net_buf_simple_add_mem(msg, srv->state->time.tai_zone_change, TAI_OF_ZONE_CHANGE_LEN);
+ } else if (model->id == BLE_MESH_MODEL_ID_TIME_SETUP_SRV) {
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->time.time_zone_offset_curr);
+ net_buf_simple_add_u8(msg, srv->state->time.time_zone_offset_new);
+ net_buf_simple_add_mem(msg, srv->state->time.tai_zone_change, TAI_OF_ZONE_CHANGE_LEN);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS:
+ if (model->id == BLE_MESH_MODEL_ID_TIME_SRV) {
+ struct bt_mesh_time_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->time.tai_utc_delta_curr);
+ net_buf_simple_add_le16(msg, srv->state->time.tai_utc_delta_new);
+ net_buf_simple_add_mem(msg, srv->state->time.tai_delta_change, TAI_OF_DELTA_CHANGE_LEN);
+ } else if (model->id == BLE_MESH_MODEL_ID_TIME_SETUP_SRV) {
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ net_buf_simple_add_le16(msg, srv->state->time.tai_utc_delta_curr);
+ net_buf_simple_add_le16(msg, srv->state->time.tai_utc_delta_new);
+ net_buf_simple_add_mem(msg, srv->state->time.tai_delta_change, TAI_OF_DELTA_CHANGE_LEN);
+ }
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ROLE_STATUS: {
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ net_buf_simple_add_u8(msg, srv->state->time_role);
+ break;
+ }
+ default:
+ BT_WARN("Unknown Time status opcode 0x%04x", opcode);
+ if (publish == false) {
+ bt_mesh_free_buf(msg);
+ }
+ return;
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void time_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_server_rsp_ctrl *rsp_ctrl = NULL;
+ uint8_t zero[5] = {0};
+ uint16_t opcode = 0U, val = 0U;
+ uint8_t prev_ttl = 0U;
+
+ if (model->user_data == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_TIME_SRV: {
+ struct bt_mesh_time_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Time Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_TIME_SETUP_SRV: {
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Time Setup Server state");
+ return;
+ }
+ rsp_ctrl = &srv->rsp_ctrl;
+ break;
+ }
+ default:
+ BT_ERR("Invalid Time Server, model id 0x%04x", model->id);
+ return;
+ }
+
+ if (rsp_ctrl->get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ if (ctx->recv_op != BLE_MESH_MODEL_OP_TIME_STATUS) {
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_TIME_GET:
+ opcode = BLE_MESH_MODEL_OP_TIME_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_TIME_STATUS: {
+ struct bt_mesh_time_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Time Server state");
+ return;
+ }
+ if (srv->state->time_role != TIME_RELAY &&
+ srv->state->time_role != TIME_CLIENT) {
+ /**
+ * If the value of the Time Role state of the element is 0x00 (None) or
+ * 0x01 (Time Authority), the message shall be ignored.
+ */
+ return;
+ }
+ if (rsp_ctrl->status_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_status_msg_t status = {0};
+ memcpy(status.time_status.tai_seconds, buf->data, TAI_SECONDS_LEN);
+ net_buf_simple_pull(buf, TAI_SECONDS_LEN);
+ if (memcmp(status.time_status.tai_seconds, zero, TAI_SECONDS_LEN)) {
+ if (buf->len != TAI_SECONDS_LEN) {
+ BT_ERR("Invalid Time Status length %d", buf->len + TAI_SECONDS_LEN);
+ return;
+ }
+ status.time_status.subsecond = net_buf_simple_pull_u8(buf);
+ status.time_status.uncertainty = net_buf_simple_pull_u8(buf);
+ val = net_buf_simple_pull_le16(buf);
+ status.time_status.time_authority = val & BIT(0);
+ status.time_status.tai_utc_delta = (val >> 1) & BIT_MASK(15);
+ status.time_status.time_zone_offset = net_buf_simple_pull_u8(buf);
+ }
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_STATUS_MSG,
+ model, ctx, (const uint8_t *)&status, sizeof(status));
+ return;
+ }
+ memcpy(srv->state->time.tai_seconds, buf->data, TAI_SECONDS_LEN);
+ net_buf_simple_pull(buf, TAI_SECONDS_LEN);
+ /**
+ * If the TAI Seconds field is 0x0000000000 the Subsecond, Uncertainty,
+ * Time Authority, TAI-UTC Delta and Time Zone Offset fields shall be
+ * omitted; otherwise these fields shall be present.
+ */
+ if (memcmp(srv->state->time.tai_seconds, zero, TAI_SECONDS_LEN)) {
+ if (buf->len != TAI_SECONDS_LEN) {
+ BT_ERR("Invalid Time Status length %d", buf->len + TAI_SECONDS_LEN);
+ return;
+ }
+ srv->state->time.subsecond = net_buf_simple_pull_u8(buf);
+ srv->state->time.uncertainty = net_buf_simple_pull_u8(buf);
+ val = net_buf_simple_pull_le16(buf);
+ srv->state->time.tai_utc_delta_curr = (val >> 1) & BIT_MASK(15);
+ srv->state->time.time_zone_offset_curr = net_buf_simple_pull_u8(buf);
+ }
+
+ bt_mesh_time_scene_server_state_change_t change = {0};
+ memcpy(change.time_status.tai_seconds, srv->state->time.tai_seconds, TAI_SECONDS_LEN);
+ change.time_status.subsecond = srv->state->time.subsecond;
+ change.time_status.uncertainty = srv->state->time.uncertainty;
+ change.time_status.time_authority = srv->state->time.time_authority;
+ change.time_status.tai_utc_delta_curr = srv->state->time.subsecond;
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (model->pub == NULL || model->pub->msg == NULL ||
+ model->pub->addr == BLE_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+ prev_ttl = model->pub->ttl;
+ if (srv->state->time_role == TIME_RELAY) {
+ /**
+ * Shall publish a Time Status message using TTL = 0 if the value of the
+ * Time Role state is 0x02 (Time Relay) and the Publish Address for the
+ * Time Server model is not set to unassigned address.
+ */
+ model->pub->ttl = 0U;
+ }
+ send_time_status(model, NULL, true, BLE_MESH_MODEL_OP_TIME_STATUS);
+ /* Restore model publication ttl value */
+ model->pub->ttl = prev_ttl;
+ return;
+ }
+ case BLE_MESH_MODEL_OP_TIME_ZONE_GET:
+ opcode = BLE_MESH_MODEL_OP_TIME_ZONE_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET:
+ opcode = BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ROLE_GET:
+ opcode = BLE_MESH_MODEL_OP_TIME_ROLE_STATUS;
+ break;
+ default:
+ BT_WARN("Unknown Time Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ send_time_status(model, ctx, false, opcode);
+}
+
+static void time_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ bt_mesh_time_scene_server_state_change_t change = {0};
+ uint16_t opcode = 0U, val = 0U;
+ uint8_t role = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_TIME_SET:
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {0};
+ memcpy(set.time_set.tai_seconds, buf->data, TAI_SECONDS_LEN);
+ net_buf_simple_pull(buf, TAI_SECONDS_LEN);
+ set.time_set.subsecond = net_buf_simple_pull_u8(buf);
+ set.time_set.uncertainty = net_buf_simple_pull_u8(buf);
+ val = net_buf_simple_pull_le16(buf);
+ set.time_set.time_authority = val & BIT(0);
+ set.time_set.tai_utc_delta = (val >> 1) & BIT_MASK(15);
+ set.time_set.time_zone_offset = net_buf_simple_pull_u8(buf);
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+ memcpy(srv->state->time.tai_seconds, buf->data, TAI_SECONDS_LEN);
+ net_buf_simple_pull(buf, TAI_SECONDS_LEN);
+ srv->state->time.subsecond = net_buf_simple_pull_u8(buf);
+ srv->state->time.uncertainty = net_buf_simple_pull_u8(buf);
+ val = net_buf_simple_pull_le16(buf);
+ srv->state->time.time_authority = val & BIT(0);
+ srv->state->time.tai_utc_delta_curr = (val >> 1) & BIT_MASK(15);
+ srv->state->time.time_zone_offset_curr = net_buf_simple_pull_u8(buf);
+ opcode = BLE_MESH_MODEL_OP_TIME_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ZONE_SET:
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {0};
+ set.time_zone_set.time_zone_offset_new = net_buf_simple_pull_u8(buf);
+ memcpy(set.time_zone_set.tai_zone_change, buf->data, TAI_OF_ZONE_CHANGE_LEN);
+ net_buf_simple_pull(buf, TAI_OF_ZONE_CHANGE_LEN);
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+ srv->state->time.time_zone_offset_new = net_buf_simple_pull_u8(buf);
+ memcpy(srv->state->time.tai_zone_change, buf->data, TAI_OF_ZONE_CHANGE_LEN);
+ net_buf_simple_pull(buf, TAI_OF_ZONE_CHANGE_LEN);
+ opcode = BLE_MESH_MODEL_OP_TIME_ZONE_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET:
+ val = net_buf_simple_pull_le16(buf);
+ if ((val >> 15) & BIT(0)) {
+ BT_ERR("Invalid Padding value 1");
+ return;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {0};
+ set.tai_utc_delta_set.tai_utc_delta_new = val & BIT_MASK(15);
+ memcpy(set.tai_utc_delta_set.tai_delta_change, buf->data, TAI_OF_DELTA_CHANGE_LEN);
+ net_buf_simple_pull(buf, TAI_OF_DELTA_CHANGE_LEN);
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+ srv->state->time.tai_utc_delta_new = val & BIT_MASK(15);
+ memcpy(srv->state->time.tai_delta_change, buf->data, TAI_OF_DELTA_CHANGE_LEN);
+ net_buf_simple_pull(buf, TAI_OF_DELTA_CHANGE_LEN);
+ opcode = BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS;
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ROLE_SET:
+ role = net_buf_simple_pull_u8(buf);
+ if (role > TIME_CLIENT) {
+ BT_ERR("Invalid Time Role 0x%02x", role);
+ return;
+ }
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {
+ .time_role_set.time_role = role,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+ srv->state->time_role = role;
+ opcode = BLE_MESH_MODEL_OP_TIME_ROLE_STATUS;
+ break;
+ default:
+ BT_ERR("Unknown Time Set opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_TIME_SET:
+ memcpy(change.time_set.tai_seconds, srv->state->time.tai_seconds, TAI_SECONDS_LEN);
+ change.time_set.subsecond = srv->state->time.subsecond;
+ change.time_set.uncertainty = srv->state->time.uncertainty;
+ change.time_set.time_authority = srv->state->time.time_authority;
+ change.time_set.tai_utc_delta_curr = srv->state->time.subsecond;
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ZONE_SET:
+ change.time_zone_set.time_zone_offset_new = srv->state->time.time_zone_offset_new;
+ memcpy(change.time_zone_set.tai_zone_change, srv->state->time.tai_zone_change, TAI_OF_ZONE_CHANGE_LEN);
+ break;
+ case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET:
+ change.tai_utc_delta_set.tai_utc_delta_new = srv->state->time.tai_utc_delta_new;
+ memcpy(change.tai_utc_delta_set.tai_delta_change, srv->state->time.tai_delta_change, TAI_OF_DELTA_CHANGE_LEN);
+ break;
+ case BLE_MESH_MODEL_OP_TIME_ROLE_SET:
+ change.time_role_set.role = srv->state->time_role;
+ break;
+ default:
+ return;
+ }
+
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ /* Send corresponding time status message */
+ send_time_status(model, ctx, false, opcode);
+}
+
+/* Scene Server & Scene Setup Server message handlers */
+static void send_scene_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ bool publish)
+{
+ struct bt_mesh_scene_srv *srv = model->user_data;
+ struct net_buf_simple *msg = NULL;
+ uint8_t length = 1 + 6;
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(length + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, length);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SCENE_STATUS);
+ /**
+ * If the message is sent as a reply to the Scene Recall message, the
+ * Status Code field identifies the result of the related operation;
+ * otherwise, the Status Code field shall be set to Success.
+ */
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_GET) {
+ net_buf_simple_add_u8(msg, SCENE_SUCCESS);
+ } else {
+ net_buf_simple_add_u8(msg, srv->state->status_code);
+ }
+ net_buf_simple_add_le16(msg, srv->state->current_scene);
+ /**
+ * When an element is in the process of changing the Scene state, the
+ * Target Scene field identifies the target Scene Number of the target
+ * Scene state the element is to reach.
+ * When an element is not in the process of changing the Scene state,
+ * the Target Scene field shall be omitted.
+ */
+ if (srv->transition.counter) {
+ bt_mesh_server_calc_remain_time(&srv->transition);
+ net_buf_simple_add_le16(msg, srv->state->target_scene);
+ net_buf_simple_add_u8(msg, srv->transition.remain_time);
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void send_scene_register_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint8_t status_code, bool publish)
+{
+ struct bt_mesh_scene_setup_srv *srv = model->user_data;
+ struct scene_register *scene = NULL;
+ struct net_buf_simple *msg = NULL;
+ uint16_t total_len = 9U;
+ int i;
+
+ if (ctx == NULL && publish == false) {
+ BT_ERR("%s, Invalid parameter", __func__);
+ return;
+ }
+
+ if (publish == false) {
+ msg = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN));
+ if (msg == NULL) {
+ BT_ERR("%s, Out of memory", __func__);
+ return;
+ }
+ } else {
+ msg = bt_mesh_server_get_pub_msg(model, 5);
+ if (msg == NULL) {
+ return;
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS);
+ net_buf_simple_add_u8(msg, status_code);
+ net_buf_simple_add_le16(msg, srv->state->current_scene);
+
+ for (i = 0; i < srv->state->scene_count; i++) {
+ scene = &srv->state->scenes[i];
+ if (scene->scene_number != INVALID_SCENE_NUMBER) {
+ total_len += SCENE_NUMBER_LEN;
+ if ((publish == false && total_len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SERVER_RSP_MAX_LEN)) ||
+ (publish == true && total_len > msg->size + BLE_MESH_SERVER_TRANS_MIC_SIZE)) {
+ /* Add this in case the message is too long */
+ BT_WARN("Too large scene register status");
+ break;
+ }
+ net_buf_simple_add_le16(msg, scene->scene_number);
+ }
+ }
+
+ if (publish == false) {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, msg, NULL, NULL));
+ bt_mesh_free_buf(msg);
+ } else {
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_publish(model));
+ }
+}
+
+static void scene_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_scene_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_SCENE_GET:
+ send_scene_status(model, ctx, false);
+ return;
+ case BLE_MESH_MODEL_OP_SCENE_REGISTER_GET:
+ /**
+ * When a Scene Server receives a Scene Register Get message, it shall
+ * respond with a Scene Register Status message, setting the Status
+ * Code field to Success.
+ */
+ send_scene_register_status(model, ctx, SCENE_SUCCESS, false);
+ return;
+ default:
+ BT_WARN("Unknown Scene Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+}
+
+void scene_publish(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, uint16_t opcode)
+{
+ struct bt_mesh_scene_srv *srv = model->user_data;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ send_scene_status(model, ctx, true);
+}
+
+static void scene_recall(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_scene_srv *srv = model->user_data;
+ struct scene_register *scene = NULL;
+ uint8_t tid = 0U, trans_time = 0U, delay = 0U;
+ uint16_t scene_number = 0U;
+ bool optional = false;
+ int64_t now = 0;
+ int i;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ scene_number = net_buf_simple_pull_le16(buf);
+ if (scene_number == INVALID_SCENE_NUMBER) {
+ BT_ERR("Invalid Scene Number 0x0000");
+ return;
+ }
+ tid = net_buf_simple_pull_u8(buf);
+
+ if (bt_mesh_server_get_optional(model, ctx, buf, &trans_time, &delay, &optional)) {
+ return;
+ }
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {
+ .scene_recall.op_en = optional,
+ .scene_recall.scene_number = scene_number,
+ .scene_recall.tid = tid,
+ .scene_recall.trans_time = trans_time,
+ .scene_recall.delay = delay,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ for (i = 0; i < srv->state->scene_count; i++) {
+ scene = &srv->state->scenes[i];
+ if (scene->scene_number == scene_number) {
+ break;
+ }
+ }
+ if (i == srv->state->scene_count) {
+ BT_WARN("Scene Number 0x%04x not exists", scene_number);
+ srv->state->status_code = SCENE_NOT_FOUND;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_RECALL) {
+ send_scene_status(model, ctx, false);
+ }
+ send_scene_status(model, ctx, true);
+ return;
+ }
+ srv->state->status_code = SCENE_SUCCESS;
+
+ /* Mesh Model Spec doesn't mention about this operation. */
+ if (bt_mesh_is_server_recv_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now)) {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_RECALL) {
+ send_scene_status(model, ctx, false);
+ }
+ send_scene_status(model, ctx, true);
+ /* In this condition, no event will be callback to application layer */
+ return;
+ }
+
+ bt_mesh_time_scene_server_lock();
+
+ bt_mesh_server_stop_transition(&srv->transition);
+ bt_mesh_server_update_last_msg(&srv->last, tid, ctx->addr, ctx->recv_dst, &now);
+
+ srv->state->in_progress = false;
+ /**
+ * When the scene transition is not in progress, the value of the Target
+ * Scene state shall be set to 0x0000.
+ */
+ srv->state->target_scene = INVALID_SCENE_NUMBER;
+
+ /**
+ * If the target state is equal to the current state, the transition
+ * shall not be started and is considered complete.
+ */
+ if (srv->state->current_scene != scene_number) {
+ scene_tt_values(srv, trans_time, delay);
+ } else {
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_RECALL) {
+ send_scene_status(model, ctx, false);
+ }
+ send_scene_status(model, ctx, true);
+
+ bt_mesh_time_scene_server_state_change_t change = {
+ .scene_recall.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ bt_mesh_time_scene_server_unlock();
+ return;
+ }
+
+ /* Copy the ctx of the received message */
+ if (srv->transition.timer.work.user_data) {
+ memcpy(srv->transition.timer.work.user_data, ctx, sizeof(struct bt_mesh_msg_ctx));
+ }
+
+ /* For Instantaneous Transition */
+ if (srv->transition.counter == 0U) {
+ srv->state->current_scene = scene_number;
+ } else {
+ /**
+ * When a scene transition is in progress, the value of the Current
+ * Scene state shall be set to 0x0000.
+ */
+ srv->state->in_progress = true;
+ srv->state->current_scene = INVALID_SCENE_NUMBER;
+ srv->state->target_scene = scene_number;
+ }
+
+ srv->transition.just_started = true;
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_RECALL) {
+ send_scene_status(model, ctx, false);
+ }
+ send_scene_status(model, ctx, true);
+
+ bt_mesh_time_scene_server_unlock();
+
+ bt_mesh_server_start_transition(&srv->transition);
+}
+
+static void scene_action(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_scene_setup_srv *srv = model->user_data;
+ struct scene_register *scene = NULL;
+ uint16_t scene_number = 0U;
+ int i;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ scene_number = net_buf_simple_pull_le16(buf);
+ if (scene_number == INVALID_SCENE_NUMBER) {
+ BT_ERR("Invalid Scene number 0x0000");
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_SCENE_STORE:
+ case BLE_MESH_MODEL_OP_SCENE_STORE_UNACK: {
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {
+ .scene_store.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+ /* Try to find a matching Scene Number */
+ for (i = 0; i < srv->state->scene_count; i++) {
+ scene = &srv->state->scenes[i];
+ if (scene->scene_number == scene_number) {
+ srv->state->status_code = SCENE_SUCCESS;
+ srv->state->current_scene = scene_number;
+ break;
+ }
+ }
+ /* Try to find a unset entry if no matching Scene Number is found */
+ if (i == srv->state->scene_count) {
+ BT_DBG("No matching Scene Number 0x%04x found", scene_number);
+ for (i = 0; i < srv->state->scene_count; i++) {
+ scene = &srv->state->scenes[i];
+ if (scene->scene_number == INVALID_SCENE_NUMBER) {
+ scene->scene_number = scene_number;
+ srv->state->status_code = SCENE_SUCCESS;
+ srv->state->current_scene = scene_number;
+ break;
+ }
+ }
+ if (i == srv->state->scene_count) {
+ BT_WARN("Scene Register is full!");
+ srv->state->status_code = SCENE_REG_FULL;
+ /* Get the Scene Number of the currently active scene */
+ for (i = 0; i < srv->state->scene_count; i++) {
+ scene = &srv->state->scenes[i];
+ if (scene->scene_number != INVALID_SCENE_NUMBER) {
+ srv->state->current_scene = scene->scene_number;
+ break;
+ }
+ }
+ if (i == srv->state->scene_count) {
+ /* A value of 0x0000 when no scene is active */
+ srv->state->current_scene = INVALID_SCENE_NUMBER;
+ }
+ }
+ }
+
+ if (srv->state->in_progress == true) {
+ /**
+ * When the scene transition is in progress and a new Scene Number is
+ * stored in the Scene Register as a result of Scene Store operation,
+ * the Target Scene state shall be set to the new Scene Number.
+ */
+ srv->state->target_scene = scene_number;
+ }
+ if (srv->state->status_code == SCENE_SUCCESS) {
+ bt_mesh_time_scene_server_state_change_t change = {
+ .scene_store.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_OP_SCENE_DELETE:
+ case BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK: {
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {
+ .scene_delete.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+ for (i = 0; i < srv->state->scene_count; i++) {
+ scene = &srv->state->scenes[i];
+ if (scene->scene_number == scene_number) {
+ scene->scene_number = INVALID_SCENE_NUMBER;
+ break;
+ }
+ }
+ if (i == srv->state->scene_count) {
+ BT_WARN("Scene Number 0x%04x not exists", scene_number);
+ /**
+ * When a Scene Server receives a Scene Delete message with the Scene
+ * Number value that does not match a Scene Number stored within the
+ * Scene Register state, it shall respond with the Scene Register
+ * Status message, setting the Status Code field to Success.
+ */
+ }
+ srv->state->status_code = SCENE_SUCCESS;
+ if (srv->state->current_scene == scene_number) {
+ /**
+ * When the Current Scene Number is deleted from a Scene Register state
+ * as a result of Scene Delete operation, the Current Scene state shall
+ * be set to 0x0000.
+ */
+ srv->state->current_scene = INVALID_SCENE_NUMBER;
+ } else {
+ /**
+ * MMDL/SR/SCES/BV-02-C requires response with Current Scene set to the
+ * latest Scene Number, but this is not mentioned in the spec.
+ *
+ * TODO: Do we need a timestamp for each newly added scene?
+ */
+ for (i = srv->state->scene_count; i > 0; i--) {
+ scene = &srv->state->scenes[i - 1];
+ if (scene->scene_number != INVALID_SCENE_NUMBER) {
+ srv->state->current_scene = scene->scene_number;
+ break;
+ }
+ }
+ if (i == 0) {
+ /* A value of 0x0000 when no scene is active */
+ srv->state->current_scene = INVALID_SCENE_NUMBER;
+ }
+ }
+
+ if (srv->state->target_scene == scene_number &&
+ srv->state->in_progress == true) {
+ /**
+ * When the scene transition is in progress and the target Scene Number
+ * is deleted from a Scene Register state as a result of Scene Delete
+ * operation, the Target Scene state shall be set to 0x0000.
+ */
+ srv->state->target_scene = INVALID_SCENE_NUMBER;
+
+ /**
+ * When a scene is deleted when a scene transition to the deleted Scene
+ * Number is in progress, the scene transition shall be terminated, but
+ * individual model transitions shall not be terminated.
+ */
+ struct bt_mesh_scene_srv *scene_srv = NULL;
+ struct bt_mesh_model *scene_model = NULL;
+
+ scene_model = bt_mesh_model_find(bt_mesh_model_elem(model), BLE_MESH_MODEL_ID_SCENE_SRV);
+ if (scene_model == NULL) {
+ BT_ERR("Scene Server not present in the element");
+ break;
+ }
+
+ scene_srv = scene_model->user_data;
+ if (scene_srv == NULL || scene_srv->state == NULL) {
+ BT_ERR("Invalid Scene Server user data");
+ break;
+ }
+
+ if (srv->state != scene_srv->state) {
+ /**
+ * Add this in case the Scene Setup Server is extending the Scene
+ * Server in another element.
+ */
+ BT_WARN("Different Scene state in Scene Server & Scene Setup Server");
+ break;
+ }
+
+ scene_srv->state->in_progress = false;
+ bt_mesh_server_stop_transition(&scene_srv->transition);
+ }
+
+ bt_mesh_time_scene_server_state_change_t change = {
+ .scene_delete.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+ break;
+ }
+ default:
+ BT_ERR("Unknown Scene setup action opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_STORE ||
+ ctx->recv_op == BLE_MESH_MODEL_OP_SCENE_DELETE) {
+ send_scene_register_status(model, ctx, srv->state->status_code, false);
+ }
+ send_scene_register_status(model, NULL, srv->state->status_code, true);
+}
+
+static uint16_t get_schedule_reg_bit(struct bt_mesh_scheduler_state *state)
+{
+ uint16_t val = 0U;
+ int i;
+
+ for (i = 0; i < state->schedule_count; i++) {
+ if (state->schedules[i].in_use) {
+ val |= (1 << i);
+ }
+ }
+
+ return val;
+}
+
+static uint64_t get_schedule_reg_state(struct bt_mesh_scheduler_state *state, uint8_t index)
+{
+ struct schedule_register *reg = &state->schedules[index];
+ uint64_t val = 0U;
+
+ val = ((uint64_t)(reg->year) << 4) | index;
+ val |= ((uint64_t)(reg->day) << 23) | ((uint64_t)(reg->month) << 11);
+ val |= ((uint64_t)(reg->minute) << 33) | ((uint64_t)(reg->hour) << 28);
+ val |= ((uint64_t)(reg->day_of_week) << 45) | ((uint64_t)(reg->second) << 39);
+ val |= ((uint64_t)(reg->trans_time) << 56) | ((uint64_t)(reg->action) << 52);
+
+ return val;
+}
+
+static void send_scheduler_act_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ uint8_t index)
+{
+ NET_BUF_SIMPLE_DEFINE(msg, 1 + 10 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+ uint64_t value = 0U;
+
+ bt_mesh_model_msg_init(&msg, BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS);
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_SCHEDULER_SRV: {
+ struct bt_mesh_scheduler_srv *srv = model->user_data;
+ value = get_schedule_reg_state(srv->state, index);
+ net_buf_simple_add_le32(&msg, (uint32_t)value);
+ net_buf_simple_add_le32(&msg, (uint32_t)(value >> 32));
+ net_buf_simple_add_le16(&msg, srv->state->schedules[index].scene_number);
+ break;
+ }
+ case BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV: {
+ struct bt_mesh_scheduler_setup_srv *srv = model->user_data;
+ value = get_schedule_reg_state(srv->state, index);
+ net_buf_simple_add_le32(&msg, (uint32_t)value);
+ net_buf_simple_add_le32(&msg, (uint32_t)(value >> 32));
+ net_buf_simple_add_le16(&msg, srv->state->schedules[index].scene_number);
+ break;
+ }
+ default:
+ BT_ERR("Invalid Scheduler Server, model id 0x%04x", model->id);
+ return;
+ }
+
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, &msg, NULL, NULL));
+}
+
+static void scheduler_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ struct bt_mesh_scheduler_srv *srv = model->user_data;
+ NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + BLE_MESH_SERVER_TRANS_MIC_SIZE);
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ switch (ctx->recv_op) {
+ case BLE_MESH_MODEL_OP_SCHEDULER_GET: {
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_GET_MSG,
+ model, ctx, NULL, 0);
+ return;
+ }
+
+ bt_mesh_model_msg_init(&msg, BLE_MESH_MODEL_OP_SCHEDULER_STATUS);
+ net_buf_simple_add_le16(&msg, get_schedule_reg_bit(srv->state));
+ BLE_MESH_CHECK_SEND_STATUS(bt_mesh_model_send(model, ctx, &msg, NULL, NULL));
+ return;
+ }
+ case BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET: {
+ uint8_t index = net_buf_simple_pull_u8(buf);
+ if (index > SCHEDULE_ENTRY_MAX_INDEX) {
+ BT_ERR("Invalid Scheduler Register Entry index 0x%02x", index);
+ return;
+ }
+
+ if (srv->rsp_ctrl.get_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_get_msg_t get = {
+ .scheduler_act_get.index = index,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_GET_MSG,
+ model, ctx, (const uint8_t *)&get, sizeof(get));
+ return;
+ }
+
+ send_scheduler_act_status(model, ctx, index);
+ return;
+ }
+ default:
+ BT_WARN("Unknown Scheduler Get opcode 0x%04x", ctx->recv_op);
+ return;
+ }
+}
+
+static void scheduler_act_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct net_buf_simple *buf)
+{
+ /**
+ * A recommended implementation of the Scheduler should calculate the value
+ * of the TAI Seconds of the next scheduled event and put it in a queue of
+ * scheduled events sorted by time. Every second, the first event in the
+ * queue is compared with the value of the Time state. The first event is
+ * executed if it is less than or equal to the Time state and then removed
+ * from the queue. After execution, the Repeat Flag shall be checked, and
+ * the next occurrence of the scheduled event is calculated and put in the
+ * queue.
+ */
+ struct bt_mesh_scheduler_setup_srv *srv = model->user_data;
+ uint8_t index = 0U, year = 0U, day = 0U, hour = 0U, minute = 0U,
+ second = 0U, day_of_week = 0U, action = 0U, trans_time = 0U;
+ uint16_t month = 0U, scene_number = 0U;
+ uint64_t value = 0U;
+
+ if (srv == NULL || srv->state == NULL) {
+ BT_ERR("%s, Invalid model user data", __func__);
+ return;
+ }
+
+ value = net_buf_simple_pull_le32(buf);
+ value |= ((uint64_t)net_buf_simple_pull_le32(buf) << 32);
+
+ index = value & BIT_MASK(4);
+ year = (value >> 4) & BIT_MASK(7);
+ month = (value >> 11) & BIT_MASK(12);
+ day = (value >> 23) & BIT_MASK(5);
+ hour = (value >> 28) & BIT_MASK(5);
+ minute = (value >> 33) & BIT_MASK(6);
+ second = (value >> 39) & BIT_MASK(6);
+ day_of_week = (value >> 45) & BIT_MASK(7);
+ action = (value >> 52) & BIT_MASK(4);
+ trans_time = (value >> 56) & BIT_MASK(8);
+
+ if (index > SCHEDULE_ENTRY_MAX_INDEX) {
+ BT_ERR("Invalid Scheduler Register Entry index 0x%02x", index);
+ return;
+ }
+
+ if (year > SCHEDULE_YEAR_ANY_YEAR) {
+ BT_ERR("Invalid Scheduler Register year 0x%02x", year);
+ return;
+ }
+
+ if (hour > SCHEDULE_HOUR_ONCE_A_DAY) {
+ BT_ERR("Invalid Scheduler Register hour 0x%02x", hour);
+ return;
+ }
+
+ if (action > SCHEDULE_ACT_SCENE_RECALL && action != SCHEDULE_ACT_NO_ACTION) {
+ BT_ERR("Invalid Scheduler Register action 0x%02x", action);
+ return;
+ }
+
+ scene_number = net_buf_simple_pull_le16(buf);
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_RSP_BY_APP) {
+ bt_mesh_time_scene_server_recv_set_msg_t set = {
+ .scheduler_act_set.index = index,
+ .scheduler_act_set.year = year,
+ .scheduler_act_set.month = month,
+ .scheduler_act_set.day = day,
+ .scheduler_act_set.hour = hour,
+ .scheduler_act_set.minute = minute,
+ .scheduler_act_set.second = second,
+ .scheduler_act_set.day_of_week = day_of_week,
+ .scheduler_act_set.action = action,
+ .scheduler_act_set.trans_time = trans_time,
+ .scheduler_act_set.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_RECV_SET_MSG,
+ model, ctx, (const uint8_t *)&set, sizeof(set));
+ return;
+ }
+
+ srv->state->schedules[index].in_use = true;
+ srv->state->schedules[index].year = year;
+ srv->state->schedules[index].month = month;
+ srv->state->schedules[index].day = day;
+ srv->state->schedules[index].hour = hour;
+ srv->state->schedules[index].minute = minute;
+ srv->state->schedules[index].second = second;
+ srv->state->schedules[index].day_of_week = day_of_week;
+ srv->state->schedules[index].action = action;
+ srv->state->schedules[index].trans_time = trans_time;
+ srv->state->schedules[index].scene_number = scene_number;
+
+ bt_mesh_time_scene_server_state_change_t change = {
+ .scheduler_act_set.index = index,
+ .scheduler_act_set.year = year,
+ .scheduler_act_set.month = month,
+ .scheduler_act_set.day = day,
+ .scheduler_act_set.hour = hour,
+ .scheduler_act_set.minute = minute,
+ .scheduler_act_set.second = second,
+ .scheduler_act_set.day_of_week = day_of_week,
+ .scheduler_act_set.action = action,
+ .scheduler_act_set.trans_time = trans_time,
+ .scheduler_act_set.scene_number = scene_number,
+ };
+ bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE,
+ model, ctx, (const uint8_t *)&change, sizeof(change));
+
+ if (ctx->recv_op == BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET) {
+ send_scheduler_act_status(model, ctx, index);
+ }
+}
+
+/* message handlers (End) */
+
+/* Mapping of message handlers for Time Server (0x1200) */
+const struct bt_mesh_model_op bt_mesh_time_srv_op[] = {
+ { BLE_MESH_MODEL_OP_TIME_GET, 0, time_get },
+ { BLE_MESH_MODEL_OP_TIME_STATUS, 5, time_get },
+ { BLE_MESH_MODEL_OP_TIME_ZONE_GET, 0, time_get },
+ { BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET, 0, time_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Time Setup Server (0x1201) */
+const struct bt_mesh_model_op bt_mesh_time_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_TIME_SET, 10, time_set },
+ { BLE_MESH_MODEL_OP_TIME_ZONE_SET, 6, time_set },
+ { BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET, 7, time_set },
+ { BLE_MESH_MODEL_OP_TIME_ROLE_GET, 0, time_get },
+ { BLE_MESH_MODEL_OP_TIME_ROLE_SET, 1, time_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Scene Server (0x1203) */
+const struct bt_mesh_model_op bt_mesh_scene_srv_op[] = {
+ { BLE_MESH_MODEL_OP_SCENE_GET, 0, scene_get },
+ { BLE_MESH_MODEL_OP_SCENE_RECALL, 3, scene_recall },
+ { BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK, 3, scene_recall },
+ { BLE_MESH_MODEL_OP_SCENE_REGISTER_GET, 0, scene_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Scene Setup Server (0x1204) */
+const struct bt_mesh_model_op bt_mesh_scene_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_SCENE_STORE, 2, scene_action },
+ { BLE_MESH_MODEL_OP_SCENE_STORE_UNACK, 2, scene_action },
+ { BLE_MESH_MODEL_OP_SCENE_DELETE, 2, scene_action },
+ { BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK, 2, scene_action },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Scheduler Server (0x1206) */
+const struct bt_mesh_model_op bt_mesh_scheduler_srv_op[] = {
+ { BLE_MESH_MODEL_OP_SCHEDULER_GET, 0, scheduler_get },
+ { BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET, 1, scheduler_get },
+ BLE_MESH_MODEL_OP_END,
+};
+
+/* Mapping of message handlers for Scheduler Setup Server (0x1207) */
+const struct bt_mesh_model_op bt_mesh_scheduler_setup_srv_op[] = {
+ { BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET, 10, scheduler_act_set },
+ { BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK, 10, scheduler_act_set },
+ BLE_MESH_MODEL_OP_END,
+};
+
+static int check_scene_server_init(struct bt_mesh_scenes_state *state)
+{
+ int i;
+
+ if (state->scene_count == 0U || state->scenes == NULL) {
+ BT_ERR("Invalid Scene state");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < state->scene_count; i++) {
+ if (state->scenes[i].scene_value == NULL) {
+ BT_ERR("Invalid Scene value, index %d", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int time_scene_server_init(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Time Scene Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_TIME_SRV: {
+ struct bt_mesh_time_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Time State");
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_TIME_SETUP_SRV: {
+ struct bt_mesh_time_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Time State");
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_SCENE_SRV: {
+ struct bt_mesh_scene_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Scene State");
+ return -EINVAL;
+ }
+
+ if (check_scene_server_init(srv->state)) {
+ return -EINVAL;
+ }
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_alloc_ctx(&srv->transition.timer.work);
+ k_delayed_work_init(&srv->transition.timer, scene_recall_work_handler);
+ }
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_SCENE_SETUP_SRV: {
+ struct bt_mesh_scene_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Scene State");
+ return -EINVAL;
+ }
+
+ if (check_scene_server_init(srv->state)) {
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_SCHEDULER_SRV: {
+ struct bt_mesh_scheduler_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Scheduler State");
+ return -EINVAL;
+ }
+
+ if (srv->state->schedule_count == 0U || srv->state->schedules == NULL) {
+ BT_ERR("Invalid Register Schedule");
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ case BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV: {
+ struct bt_mesh_scheduler_setup_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Scheduler State");
+ return -EINVAL;
+ }
+
+ if (srv->state->schedule_count == 0U || srv->state->schedules == NULL) {
+ BT_ERR("Invalid Register Schedule");
+ return -EINVAL;
+ }
+
+ srv->model = model;
+ break;
+ }
+ default:
+ BT_WARN("Unknown Time Scene Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ bt_mesh_mutex_create(&time_scene_server_lock);
+
+ return 0;
+}
+
+static int time_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Time Server has no publication support");
+ return -EINVAL;
+ }
+
+ /**
+ * When this model is present on an Element, the corresponding Time Setup
+ * Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_TIME_SETUP_SRV) == NULL) {
+ BT_WARN("Time Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return time_scene_server_init(model);
+}
+
+static int time_setup_srv_init(struct bt_mesh_model *model)
+{
+ /* This model does not support subscribing nor publishing */
+ if (model->pub) {
+ BT_ERR("Time Setup Server shall not support publication");
+ return -EINVAL;
+ }
+
+ return time_scene_server_init(model);
+}
+
+static int scene_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Scene Server has no publication support");
+ return -EINVAL;
+ }
+
+ /* The model may be present only on the Primary element of a node. */
+ if (!bt_mesh_model_in_primary(model)) {
+ BT_WARN("Scene Server not on the Primary element");
+ /* Just give a warning here, continue with the initialization */
+ }
+ /**
+ * When this model is present on an Element, the corresponding Scene Setup
+ * Server model shall also be present.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_SCENE_SETUP_SRV) == NULL) {
+ BT_WARN("Scene Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return time_scene_server_init(model);
+}
+
+static int scene_setup_srv_init(struct bt_mesh_model *model)
+{
+ /* The model may be present only on the Primary element of a node. */
+ if (!bt_mesh_model_in_primary(model)) {
+ BT_WARN("Scene Setup Server not on the Primary element");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return time_scene_server_init(model);
+}
+
+static int scheduler_srv_init(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Scheduler Server has no publication support");
+ return -EINVAL;
+ }
+
+ /* The model may be present only on the Primary element of a node. */
+ if (!bt_mesh_model_in_primary(model)) {
+ BT_WARN("Scheduler Server not on the Primary element");
+ /* Just give a warning here, continue with the initialization */
+ }
+ /**
+ * When this model is present on an Element, the corresponding Scheduler
+ * Setup Server model shall also be present. The model requires the Time
+ * Server model shall be present on the element.
+ */
+ struct bt_mesh_elem *element = bt_mesh_model_elem(model);
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV) == NULL) {
+ BT_WARN("Scheduler Setup Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ if (bt_mesh_model_find(element, BLE_MESH_MODEL_ID_TIME_SRV) == NULL) {
+ BT_WARN("Time Server not present");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return time_scene_server_init(model);
+}
+
+static int scheduler_setup_srv_init(struct bt_mesh_model *model)
+{
+ /* The model may be present only on the Primary element of a node. */
+ if (!bt_mesh_model_in_primary(model)) {
+ BT_WARN("Scheduler Setup Server not on the Primary element");
+ /* Just give a warning here, continue with the initialization */
+ }
+ return time_scene_server_init(model);
+}
+
+#if CONFIG_BLE_MESH_DEINIT
+static int time_scene_server_deinit(struct bt_mesh_model *model)
+{
+ if (model->user_data == NULL) {
+ BT_ERR("Invalid Time Scene Server user data, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ switch (model->id) {
+ case BLE_MESH_MODEL_ID_SCENE_SRV: {
+ struct bt_mesh_scene_srv *srv = model->user_data;
+ if (srv->state == NULL) {
+ BT_ERR("Invalid Scene State");
+ return -EINVAL;
+ }
+
+ if (check_scene_server_init(srv->state)) {
+ return -EINVAL;
+ }
+
+ if (srv->rsp_ctrl.set_auto_rsp == BLE_MESH_SERVER_AUTO_RSP) {
+ bt_mesh_server_free_ctx(&srv->transition.timer.work);
+ k_delayed_work_free(&srv->transition.timer);
+ }
+ break;
+ }
+ case BLE_MESH_MODEL_ID_TIME_SRV:
+ case BLE_MESH_MODEL_ID_TIME_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_SCENE_SETUP_SRV:
+ case BLE_MESH_MODEL_ID_SCHEDULER_SRV:
+ case BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV:
+ break;
+ default:
+ BT_WARN("Unknown Time Scene Server, model id 0x%04x", model->id);
+ return -EINVAL;
+ }
+
+ bt_mesh_mutex_free(&time_scene_server_lock);
+
+ return 0;
+}
+
+static int time_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Time Server has no publication support");
+ return -EINVAL;
+ }
+
+ return time_scene_server_deinit(model);
+}
+
+static int time_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub) {
+ BT_ERR("Time Setup Server shall not support publication");
+ return -EINVAL;
+ }
+
+ return time_scene_server_deinit(model);
+}
+
+static int scene_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Scene Server has no publication support");
+ return -EINVAL;
+ }
+
+ return time_scene_server_deinit(model);
+}
+
+static int scene_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return time_scene_server_deinit(model);
+}
+
+static int scheduler_srv_deinit(struct bt_mesh_model *model)
+{
+ if (model->pub == NULL) {
+ BT_ERR("Scheduler Server has no publication support");
+ return -EINVAL;
+ }
+
+ return time_scene_server_deinit(model);
+}
+
+static int scheduler_setup_srv_deinit(struct bt_mesh_model *model)
+{
+ return time_scene_server_deinit(model);
+}
+#endif /* CONFIG_BLE_MESH_DEINIT */
+
+const struct bt_mesh_model_cb bt_mesh_time_srv_cb = {
+ .init = time_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = time_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_time_setup_srv_cb = {
+ .init = time_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = time_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_scene_srv_cb = {
+ .init = scene_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = scene_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_scene_setup_srv_cb = {
+ .init = scene_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = scene_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_scheduler_srv_cb = {
+ .init = scheduler_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = scheduler_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+const struct bt_mesh_model_cb bt_mesh_scheduler_setup_srv_cb = {
+ .init = scheduler_setup_srv_init,
+#if CONFIG_BLE_MESH_DEINIT
+ .deinit = scheduler_setup_srv_deinit,
+#endif /* CONFIG_BLE_MESH_DEINIT */
+};
+
+#endif /* CONFIG_BLE_MESH_TIME_SCENE_SERVER */