/* * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include "btstack/bluetooth.h" #include "system/includes.h" #include "bt_common.h" #include "api/sig_mesh_api.h" #include "api/scene_srv.h" #include "misc/slist.h" #include "misc/list_gen.h" #include "misc/byteorder.h" #include "model_api.h" #include #include "model_utils.h" #include "model_types.h" #include "adaptation.h" #if (CONFIG_BT_MESH_SCENE_SRV) #define LOG_TAG "[Gen_Scene_srv]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include "debug.h" #define SCENE_PAGE_SIZE SETTINGS_MAX_VAL_LEN /* Account for company ID in data: */ #define VND_MODEL_SCENE_DATA_OVERHEAD sizeof(uint16_t) struct __attribute__((__packed__)) scene_data { uint8_t len; uint8_t elem_idx; uint16_t id; uint8_t data[]; }; static sys_slist_t scene_servers; static char *scene_path(char *buf, uint16_t scene, bool vnd, uint8_t page) { sprintf(buf, "%x/%c%x", scene, vnd ? 'v' : 's', page); return buf; } static inline void update_page_count(struct bt_mesh_scene_srv *srv, bool vnd, uint8_t page) { if (vnd) { srv->vndpages = MAX(page + 1, srv->vndpages); } else { srv->sigpages = MAX(page + 1, srv->sigpages); } } static const struct bt_mesh_scene_entry * entry_find(const struct bt_mesh_model *mod, bool vnd) { const struct bt_mesh_scene_entry *it; if (vnd) { extern const struct bt_mesh_scene_entry _bt_mesh_scene_entry_vnd_list_start[]; extern const struct bt_mesh_scene_entry _bt_mesh_scene_entry_vnd_list_end[]; for (it = _bt_mesh_scene_entry_vnd_list_start; it != _bt_mesh_scene_entry_vnd_list_end; it++) { if (it->id.vnd.id == mod->vnd.id && it->id.vnd.company == mod->vnd.company) { return it; } } } else { extern const struct bt_mesh_scene_entry _bt_mesh_scene_entry_sig_list_start[]; extern const struct bt_mesh_scene_entry _bt_mesh_scene_entry_sig_list_end[]; for (it = _bt_mesh_scene_entry_sig_list_start; it != _bt_mesh_scene_entry_sig_list_end; it++) { if (it->id.sig == mod->id) { return it; } } } return NULL; } static struct bt_mesh_scene_srv *srv_find(uint16_t elem_idx) { struct bt_mesh_scene_srv *srv = scene_servers.head; // 遍历链表 while (srv != NULL) { /* Scene servers are added to the link list in reverse * composition data order. The first scene server that isn't * after this element will be the right one: */ if (srv->model->rt->elem_idx <= elem_idx) { return srv; } srv = (struct bt_mesh_scene_srv *)srv->n.next; } return NULL; } static uint16_t current_scene(const struct bt_mesh_scene_srv *srv) { if (srv->next && k_work_delayable_is_pending(&srv->work)) { /* When we're in a transition, the current scene should be NONE (see the Bluetooth * mesh model specification, section 5.1.3.2.1). Check that we're not just in a * delay phase: */ if (!srv->transition.delay || (k_ticks_to_ms_near64(k_work_delayable_remaining_get(&srv->work)) <= srv->transition.time)) { return BT_MESH_SCENE_NONE; } else { return srv->prev; } } return srv->prev; } static uint16_t target_scene(const struct bt_mesh_scene_srv *srv) { return srv->next; } static void scene_status_encode(struct bt_mesh_scene_srv *srv, struct net_buf_simple *buf, const struct bt_mesh_scene_state *state) { bt_mesh_model_msg_init(buf, BT_MESH_SCENE_OP_STATUS); net_buf_simple_add_u8(buf, state->status); if (state->target != BT_MESH_SCENE_NONE) { net_buf_simple_add_le16(buf, state->current); net_buf_simple_add_le16(buf, state->target); net_buf_simple_add_u8(buf, model_transition_encode(state->remaining_time)); } else { net_buf_simple_add_le16(buf, state->current); } } static int scene_status_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, const struct bt_mesh_scene_state *state) { struct bt_mesh_scene_srv *srv = model->rt->user_data; BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_STATUS, BT_MESH_SCENE_MSG_MAXLEN_STATUS); scene_status_encode(srv, &buf, state); return bt_mesh_msg_send(model, ctx, &buf); } static void curr_scene_state_get(struct bt_mesh_scene_srv *srv, struct bt_mesh_scene_state *state) { state->current = current_scene(srv); state->target = target_scene(srv); state->remaining_time = k_ticks_to_ms_near64(k_work_delayable_remaining_get(&srv->work)); state->status = BT_MESH_SCENE_SUCCESS; } static int handle_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_srv *srv = model->rt->user_data; struct bt_mesh_scene_state state; curr_scene_state_get(srv, &state); scene_status_send(model, ctx, &state); return 0; } static int scene_recall(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf, bool ack) { struct bt_mesh_scene_srv *srv = model->rt->user_data; struct bt_mesh_model_transition transition; struct bt_mesh_scene_state state; uint16_t scene; bool has_trans; uint8_t tid; int err; scene = net_buf_simple_pull_le16(buf); if (scene == BT_MESH_SCENE_NONE) { return -EINVAL; /* Prohibited */ } tid = net_buf_simple_pull_u8(buf); has_trans = !!model_transition_get(srv->model, &transition, buf); if (tid_check_and_update(&srv->tid, tid, ctx)) { log_debug("Duplicate TID"); curr_scene_state_get(srv, &state); scene_status_send(model, ctx, &state); return 0; } err = bt_mesh_scene_srv_set(srv, scene, has_trans ? &transition : NULL); if (err) { curr_scene_state_get(srv, &state); if (err == -ENOENT) { state.status = BT_MESH_SCENE_NOT_FOUND; } } else { state.status = BT_MESH_SCENE_SUCCESS; state.current = srv->prev; state.target = srv->next; state.remaining_time = transition.time; } if (ack) { scene_status_send(model, ctx, &state); } if (state.status == BT_MESH_SCENE_SUCCESS) { /* Publish */ scene_status_send(model, NULL, &state); } return 0; } static int handle_recall(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { return scene_recall(model, ctx, buf, true); } static int handle_recall_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { return scene_recall(model, ctx, buf, false); } static int scene_register_status_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, enum bt_mesh_scene_status status) { struct bt_mesh_scene_srv *srv = model->rt->user_data; BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_REGISTER_STATUS, BT_MESH_SCENE_MSG_MINLEN_REGISTER_STATUS + 2 * CONFIG_BT_MESH_SCENES_MAX); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_REGISTER_STATUS); net_buf_simple_add_u8(&buf, status); net_buf_simple_add_le16(&buf, current_scene(srv)); for (int i = 0; i < srv->count; i++) { net_buf_simple_add_le16(&buf, srv->all[i]); } return bt_mesh_msg_send(model, ctx, &buf); } static int handle_register_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { (void)scene_register_status_send(model, ctx, BT_MESH_SCENE_SUCCESS); return 0; } const struct bt_mesh_model_op _bt_mesh_scene_srv_op[] = { { BT_MESH_SCENE_OP_GET, BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_GET), handle_get, }, { BT_MESH_SCENE_OP_RECALL, BT_MESH_LEN_MIN(BT_MESH_SCENE_MSG_MINLEN_RECALL), handle_recall, }, { BT_MESH_SCENE_OP_RECALL_UNACK, BT_MESH_LEN_MIN(BT_MESH_SCENE_MSG_MINLEN_RECALL), handle_recall_unack, }, { BT_MESH_SCENE_OP_REGISTER_GET, BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_REGISTER_GET), handle_register_get, }, BT_MESH_MODEL_OP_END, }; static uint16_t *scene_find(struct bt_mesh_scene_srv *srv, uint16_t scene) { for (int i = 0; i < srv->count; i++) { if (srv->all[i] == scene) { return &srv->all[i]; } } return NULL; } static void entry_recover(struct bt_mesh_scene_srv *srv, bool vnd, const struct scene_data *data) { const struct bt_mesh_elem *elem = &bt_mesh_comp_get()->elem[data->elem_idx]; const size_t overhead = vnd ? VND_MODEL_SCENE_DATA_OVERHEAD : 0; const struct bt_mesh_scene_entry *entry; struct bt_mesh_model *mod; if (vnd) { mod = bt_mesh_model_find_vnd(elem, sys_get_le16(data->data), data->id); } else { mod = bt_mesh_model_find(elem, data->id); } if (!mod) { log_error("No model @%s", bt_hex(&data->elem_idx, vnd ? 5 : 3)); return; } /* MeshMDL1.0.1, section 5.1.3.1.1: * If a model is extending another model, the extending model shall determine * the Stored with Scene behavior of that model. */ if (bt_mesh_model_is_extended(mod)) { return; } entry = entry_find(mod, vnd); if (!entry) { log_error("No scene entry for %s", bt_hex(&data->elem_idx, vnd ? 5 : 3)); return; } entry->recall(mod, &data->data[overhead], data->len - overhead, &srv->transition); } static void page_recover(struct bt_mesh_scene_srv *srv, bool vnd, const uint8_t buf[], size_t len) { for (struct scene_data *data = (struct scene_data *)&buf[0]; data < (struct scene_data *)&buf[len]; data = (struct scene_data *)&data->data[data->len]) { entry_recover(srv, vnd, data); } } static ssize_t entry_store(struct bt_mesh_model *mod, const struct bt_mesh_scene_entry *entry, bool vnd, uint8_t buf[]) { struct scene_data *data = (struct scene_data *)buf; ssize_t size; data->elem_idx = mod->rt->elem_idx; if (vnd) { data->id = mod->vnd.id; sys_put_le16(mod->vnd.company, data->data); size = entry->store(mod, &data->data[VND_MODEL_SCENE_DATA_OVERHEAD]); data->len = size + VND_MODEL_SCENE_DATA_OVERHEAD; } else { data->id = mod->id; size = entry->store(mod, &data->data[0]); data->len = size; } if (size > entry->maxlen) { log_error("Entry %s:%u:%u: data too large (%u bytes)", vnd ? "vnd" : "sig", mod->rt->elem_idx, mod->rt->mod_idx, size); return -EINVAL; } if (size < 0) { log_error("Failed storing %s:%u:%u (%d)", vnd ? "vnd" : "sig", mod->rt->elem_idx, mod->rt->mod_idx, size); return size; } if (size == 0) { /* Silently ignore this entry */ return 0; } return sizeof(struct scene_data) + data->len; } /** Store a single page of the Scene. * * To accommodate large scene data, each scene is stored in pages of up to 256 * bytes. */ static void page_store(struct bt_mesh_scene_srv *srv, uint16_t scene, uint8_t page, bool vnd, uint8_t buf[], size_t len) { char path[9]; int err; scene_path(path, scene, vnd, page); update_page_count(srv, vnd, page); err = bt_mesh_model_data_store(srv->model, false, path, buf, len); if (err) { log_error("Failed storing %s: %d", path, err); } } /** @brief Get the end of the Scene server's controlled elements. * * A Scene Server controls all elements whose index is equal to or larger than * its own, and smaller than the return value of this function. * * @param[in] srv Scene Server to find control boundary of. * * @return The element index of the next Scene Server, or the total element * count. */ static uint16_t srv_elem_end(const struct bt_mesh_scene_srv *srv) { uint16_t end = bt_mesh_elem_count(); struct bt_mesh_scene_srv *it = scene_servers.head; // 遍历链表 while (it != NULL) { if (it == srv) { break; } end = it->model->rt->elem_idx; it = (struct bt_mesh_scene_srv *)it->n.next; } return end; } static void scene_recall_complete_mod(struct bt_mesh_scene_srv *srv, struct bt_mesh_model *models, int model_count, bool vnd) { for (int j = 0; j < model_count; j++) { const struct bt_mesh_scene_entry *entry; struct bt_mesh_model *mod = &models[j]; if (mod == srv->model) { continue; } /* MeshMDL1.0.1, section 5.1.3.1.1: * If a model is extending another model, the extending * model shall determine the Stored with Scene behavior * of that model. */ if (bt_mesh_model_is_extended(mod)) { continue; } entry = entry_find(mod, vnd); if (!entry || !entry->recall_complete) { continue; } entry->recall_complete(mod); } } static void scene_recall_complete(struct bt_mesh_scene_srv *srv) { const struct bt_mesh_comp *comp = bt_mesh_comp_get(); uint16_t elem_end = srv_elem_end(srv); for (int i = srv->model->rt->elem_idx; i < elem_end; i++) { const struct bt_mesh_elem *elem = &comp->elem[i]; scene_recall_complete_mod(srv, elem->models, elem->model_count, false); scene_recall_complete_mod(srv, elem->vnd_models, elem->vnd_model_count, true); } } static void scene_store_mod(struct bt_mesh_scene_srv *srv, uint16_t scene, bool vnd) { const size_t data_overhead = sizeof(struct scene_data) + (vnd ? 2 : 0); const struct bt_mesh_comp *comp = bt_mesh_comp_get(); uint16_t elem_end = srv_elem_end(srv); uint8_t buf[SCENE_PAGE_SIZE]; uint8_t page = 0; size_t len = 0; for (int i = srv->model->rt->elem_idx; i < elem_end; i++) { const struct bt_mesh_elem *elem = &comp->elem[i]; struct bt_mesh_model *models = vnd ? elem->vnd_models : elem->models; int model_count = vnd ? elem->vnd_model_count : elem->model_count; for (int j = 0; j < model_count; j++) { const struct bt_mesh_scene_entry *entry; struct bt_mesh_model *mod = &models[j]; ssize_t size; if (mod == srv->model) { continue; } /* MeshMDL1.0.1, section 5.1.3.1.1: * If a model is extending another model, the extending * model shall determine the Stored with Scene behavior * of that model. */ if (bt_mesh_model_is_extended(mod)) { continue; } entry = entry_find(mod, vnd); if (!entry) { continue; } if (len + data_overhead + entry->maxlen >= SCENE_PAGE_SIZE) { page_store(srv, scene, page++, vnd, buf, len); len = 0; } size = entry_store(mod, entry, vnd, &buf[len]); len += MAX(0, size); } } if (len) { page_store(srv, scene, page, vnd, buf, len); } } static enum bt_mesh_scene_status scene_store(struct bt_mesh_scene_srv *srv, uint16_t scene) { uint16_t *existing = scene_find(srv, scene); if (!existing) { if (srv->count == ARRAY_SIZE(srv->all)) { log_error("Out of space"); return BT_MESH_SCENE_REGISTER_FULL; } srv->all[srv->count++] = scene; } scene_store_mod(srv, scene, false); scene_store_mod(srv, scene, true); srv->prev = scene; srv->next = BT_MESH_SCENE_NONE; /* We're checking srv->next in the handler, so failure to cancel is okay: */ (void)k_work_cancel_delayable(&srv->work); return BT_MESH_SCENE_SUCCESS; } static void scene_delete(struct bt_mesh_scene_srv *srv, uint16_t *scene) { uint8_t path[9]; log_debug("0x%x", *scene); for (int i = 0; i < srv->sigpages; i++) { scene_path(path, *scene, false, i); (void)bt_mesh_model_data_store(srv->model, false, path, NULL, 0); } for (int i = 0; i < srv->vndpages; i++) { scene_path(path, *scene, true, i); (void)bt_mesh_model_data_store(srv->model, false, path, NULL, 0); } uint16_t target = target_scene(srv); uint16_t current = current_scene(srv); if (target == *scene || (current == *scene && target == BT_MESH_SCENE_NONE)) { srv->next = BT_MESH_SCENE_NONE; srv->prev = BT_MESH_SCENE_NONE; /* Cancel failure checked in work handler. */ (void)k_work_cancel_delayable(&srv->work); } else if (current == *scene && target != *scene) { srv->prev = BT_MESH_SCENE_NONE; } *scene = srv->all[--srv->count]; } static int handle_store(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_srv *srv = model->rt->user_data; enum bt_mesh_scene_status status; uint16_t scene_number; scene_number = net_buf_simple_pull_le16(buf); if (scene_number == BT_MESH_SCENE_NONE) { return -EINVAL; } status = scene_store(srv, scene_number); scene_register_status_send(model, ctx, status); return 0; } static int handle_store_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_srv *srv = model->rt->user_data; uint16_t scene_number; scene_number = net_buf_simple_pull_le16(buf); if (scene_number == BT_MESH_SCENE_NONE) { return -EINVAL; } (void)scene_store(srv, scene_number); return 0; } static int delete_scene(struct bt_mesh_scene_srv *srv, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { uint16_t *scene; scene = scene_find(srv, net_buf_simple_pull_le16(buf)); if (scene != BT_MESH_SCENE_NONE) { scene_delete(srv, scene); } return 0; } static int handle_delete(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_srv *srv = model->rt->user_data; int err; err = delete_scene(srv, ctx, buf); if (err) { return err; } scene_register_status_send(model, ctx, BT_MESH_SCENE_SUCCESS); return 0; } static int handle_delete_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_srv *srv = model->rt->user_data; return delete_scene(srv, ctx, buf); } const struct bt_mesh_model_op _bt_mesh_scene_setup_srv_op[] = { { BT_MESH_SCENE_OP_STORE, BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_STORE), handle_store, }, { BT_MESH_SCENE_OP_STORE_UNACK, BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_STORE), handle_store_unack, }, { BT_MESH_SCENE_OP_DELETE, BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_DELETE), handle_delete, }, { BT_MESH_SCENE_OP_DELETE_UNACK, BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_DELETE), handle_delete_unack, }, BT_MESH_MODEL_OP_END, }; // static void scene_srv_transition_end(struct k_work *work) // { // struct bt_mesh_scene_srv *srv = // CONTAINER_OF(k_work_delayable_from_work(work), struct bt_mesh_scene_srv, work); // if (srv->next == BT_MESH_SCENE_NONE) { // /* This is already done */ // return; // } // srv->prev = srv->next; // srv->next = BT_MESH_SCENE_NONE; // /* Publish at the end of the transition */ // if (bt_mesh_model_transition_time(&srv->transition)) { // bt_mesh_scene_srv_pub(srv, NULL); // } // } static int scene_srv_pub_update(struct bt_mesh_model *model) { struct bt_mesh_scene_srv *srv = model->rt->user_data; struct bt_mesh_scene_state state; curr_scene_state_get(srv, &state); scene_status_encode(srv, &srv->pub_msg, &state); return 0; } static int scene_srv_init(struct bt_mesh_model *model) { struct bt_mesh_scene_srv *srv = model->rt->user_data; sys_slist_prepend(&scene_servers, &srv->n); srv->model = model; // k_work_init_delayable(&srv->work, scene_srv_transition_end); net_buf_simple_init_with_data(&srv->pub_msg, srv->buf, sizeof(srv->buf)); srv->pub.msg = &srv->pub_msg; srv->pub.update = scene_srv_pub_update; return 0; } static int scene_srv_set(struct bt_mesh_model *model, const char *path, size_t len_rd, settings_read_cb read_cb, void *cb_arg) { #if 0 // not adapter struct bt_mesh_scene_srv *srv = model->rt->user_data; uint8_t buf[SCENE_PAGE_SIZE]; uint16_t scene; ssize_t size; uint8_t page; bool vnd; log_debug("path: %s", path); /* The entire model data tree is loaded in this callback. Depending on * the path and whether we have started the mesh, we'll handle the data * differently: * * - Path "XXXX/vYY": Scene XXXX vendor model page YY * - Path "XXXX/sYY": Scene XXXX sig model page YY */ scene = strtol(path, NULL, 16); if (scene == BT_MESH_SCENE_NONE) { log_error("Unknown data %s", path); return 0; } settings_name_next(path, &path); if (!path) { return 0; } vnd = path[0] == 'v'; page = strtol(&path[1], NULL, 16); update_page_count(srv, vnd, page); /* Before starting the mesh, we'll just register that the scene exists: * Once the mesh starts, we'll load the current scene, and end up in * this callback again, but bt_mesh_is_provisioned() will be true. */ if (!bt_mesh_is_provisioned()) { if (scene_find(srv, scene)) { return 0; } if (srv->count == ARRAY_SIZE(srv->all)) { log_error("No room for scene 0x%x", scene); return 0; } log_debug("Recovered scene 0x%x", scene); srv->all[srv->count++] = scene; return 0; } size = read_cb(cb_arg, &buf, sizeof(buf)); if (size < 0) { log_error("Failed loading scene 0x%x", scene); return -EINVAL; } log_debug("0x%x: %s", scene, bt_hex(buf, size)); page_recover(srv, vnd, buf, size); #endif return 0; } static void scene_srv_reset(struct bt_mesh_model *model) { struct bt_mesh_scene_srv *srv = model->rt->user_data; srv->next = BT_MESH_SCENE_NONE; while (srv->count) { scene_delete(srv, &srv->all[0]); } srv->prev = BT_MESH_SCENE_NONE; /* We're checking srv->next in the handler, so failure to cancel is okay: */ (void)k_work_cancel_delayable(&srv->work); srv->sigpages = 0; srv->vndpages = 0; } const struct bt_mesh_model_cb _bt_mesh_scene_srv_cb = { .init = scene_srv_init, .settings_set = scene_srv_set, .reset = scene_srv_reset, }; static int scene_setup_srv_init(struct bt_mesh_model *model) { struct bt_mesh_scene_srv *srv = model->rt->user_data; struct bt_mesh_dtt_srv *dtt_srv = NULL; int err; if (!srv) { return -EINVAL; } srv->setup_mod = model; //for compiler, not used now. // if (IS_ENABLED(CONFIG_BT_MESH_DTT_SRV)) { // dtt_srv = bt_mesh_dtt_srv_get(bt_mesh_model_elem(model)); // } // if (!dtt_srv) { // log_error("Failed to find Generic DTT Server on element"); // return -EINVAL; // } // err = bt_mesh_model_extend(srv->setup_mod, dtt_srv->model); // if (err) { // return err; // } //for compiler, not used now. #if CONFIG_BT_MESH_COMP_PAGE_1 err = bt_mesh_model_correspond(srv->setup_mod, srv->model); if (err) { return err; } #endif #if CONFIG_BT_MESH_MODEL_EXTENSIONS return bt_mesh_model_extend(srv->setup_mod, srv->model); #else return 0; #endif } const struct bt_mesh_model_cb _bt_mesh_scene_setup_srv_cb = { .init = scene_setup_srv_init, }; void bt_mesh_scene_invalidate(struct bt_mesh_model *mod) { struct bt_mesh_scene_srv *srv = srv_find(mod->rt->elem_idx); if (!srv) { return; } srv->prev = BT_MESH_SCENE_NONE; /* We're checking srv->next in the handler, so failure to cancel is okay: */ (void)k_work_cancel_delayable(&srv->work); srv->next = BT_MESH_SCENE_NONE; } int bt_mesh_scene_srv_set(struct bt_mesh_scene_srv *srv, uint16_t scene, struct bt_mesh_model_transition *transition) { int32_t transition_time; uint16_t curr; char path[25]; int err; if (scene == BT_MESH_SCENE_NONE || model_transition_is_invalid(transition)) { return -EINVAL; } if (!scene_find(srv, scene)) { log_error("Unknown scene 0x%x", scene); return -ENOENT; } curr = current_scene(srv); if (scene == curr) { srv->prev = scene; srv->next = BT_MESH_SCENE_NONE; srv->transition.time = 0; srv->transition.delay = 0; /* We're checking srv->next in the handler, so failure to cancel is okay: */ (void)k_work_cancel_delayable(&srv->work); return 0; } transition_time = bt_mesh_model_transition_time(transition); if (transition_time) { srv->transition = *transition; srv->prev = curr; srv->next = scene; k_work_reschedule(&srv->work, K_MSEC(transition_time)); } else { srv->prev = scene; srv->next = BT_MESH_SCENE_NONE; srv->transition.time = 0; srv->transition.delay = 0; /* We're checking srv->next in the handler, so failure to cancel is okay: */ (void)k_work_cancel_delayable(&srv->work); } sprintf(path, "bt/mesh/s/%x/data/%x", (srv->model->rt->elem_idx << 8) | srv->model->rt->mod_idx, scene); log_debug("Loading %s", path); //for compiler, not used now. // err = settings_load_subtree(path); // if (!err) { // scene_recall_complete(srv); // } return err; } int bt_mesh_scene_srv_pub(struct bt_mesh_scene_srv *srv, struct bt_mesh_msg_ctx *ctx) { struct bt_mesh_scene_state state; curr_scene_state_get(srv, &state); return scene_status_send(srv->model, ctx, &state); } uint16_t bt_mesh_scene_srv_current_scene_get(const struct bt_mesh_scene_srv *srv) { return current_scene(srv); } uint16_t bt_mesh_scene_srv_target_scene_get(const struct bt_mesh_scene_srv *srv) { return target_scene(srv); } #endif /* CONFIG_BT_MESH_SCENE_SRV */