/* * 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 "model_api.h" #include "msg.h" #include "model_utils.h" #if (CONFIG_BT_MESH_SCENE_CLI) #define LOG_TAG "[Scene_cli]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include "debug.h" static int handle_scene_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_state *rsp; struct bt_mesh_scene_cli *cli = model->rt->user_data; struct bt_mesh_scene_state state; state.status = net_buf_simple_pull_u8(buf); state.current = net_buf_simple_pull_le16(buf); if (buf->len == 3) { state.target = net_buf_simple_pull_le16(buf); state.remaining_time = model_transition_decode(net_buf_simple_pull_u8(buf)); } else if (!buf->len) { state.target = BT_MESH_SCENE_NONE; state.remaining_time = 0; } else { /* Invalid size */ return -EMSGSIZE; } if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, BT_MESH_SCENE_OP_STATUS, ctx->addr, (void **)&rsp)) { *rsp = state; bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx); } if (cli->status) { cli->status(cli, ctx, &state); } return 0; } static int handle_scene_reg(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_scene_register *rsp; struct bt_mesh_scene_cli *cli = model->rt->user_data; struct bt_mesh_scene_register reg; reg.status = net_buf_simple_pull_u8(buf); reg.current = net_buf_simple_pull_le16(buf); if (buf->len % 2) { return -EMSGSIZE; } reg.count = buf->len / sizeof(uint16_t); reg.scenes = net_buf_simple_pull_mem(buf, buf->len); if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, BT_MESH_SCENE_OP_REGISTER_STATUS, ctx->addr, (void **)&rsp)) { rsp->status = reg.status; rsp->current = reg.current; if (rsp->scenes) { rsp->count = MIN(rsp->count, reg.count); memcpy(rsp->scenes, reg.scenes, rsp->count * sizeof(uint16_t)); } else { rsp->count = 0; } bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx); } if (cli->scene_register) { cli->scene_register(cli, ctx, ®); } return 0; } const struct bt_mesh_model_op _bt_mesh_scene_cli_op[] = { { BT_MESH_SCENE_OP_STATUS, BT_MESH_LEN_MIN(BT_MESH_SCENE_MSG_MINLEN_STATUS), handle_scene_status, }, { BT_MESH_SCENE_OP_REGISTER_STATUS, BT_MESH_LEN_MIN(BT_MESH_SCENE_MSG_MINLEN_REGISTER_STATUS), handle_scene_reg, }, BT_MESH_MODEL_OP_END, }; static int scene_cli_init(struct bt_mesh_model *model) { struct bt_mesh_scene_cli *cli = model->rt->user_data; if (!cli) { return -EINVAL; } cli->model = model; net_buf_simple_init_with_data(&cli->pub_msg, cli->buf, sizeof(cli->buf)); cli->pub.msg = &cli->pub_msg; bt_mesh_msg_ack_ctx_init(&cli->ack_ctx); return 0; } static void scene_cli_reset(struct bt_mesh_model *model) { struct bt_mesh_scene_cli *cli = model->rt->user_data; net_buf_simple_reset(cli->pub.msg); bt_mesh_msg_ack_ctx_reset(&cli->ack_ctx); } const struct bt_mesh_model_cb _bt_mesh_scene_cli_cb = { .init = scene_cli_init, .reset = scene_cli_reset, }; int bt_mesh_scene_cli_get(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, struct bt_mesh_scene_state *rsp) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_GET, BT_MESH_SCENE_MSG_LEN_GET); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_GET); struct bt_mesh_msg_rsp_ctx rsp_ctx = { .ack = &cli->ack_ctx, .op = BT_MESH_SCENE_OP_STATUS, .user_data = rsp, .timeout = model_ackd_timeout_get(cli->model, ctx), }; return bt_mesh_msg_ackd_send(cli->model, ctx, &buf, rsp ? &rsp_ctx : NULL); } int bt_mesh_scene_cli_register_get(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, struct bt_mesh_scene_register *rsp) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_REGISTER_GET, BT_MESH_SCENE_MSG_LEN_REGISTER_GET); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_REGISTER_GET); struct bt_mesh_msg_rsp_ctx rsp_ctx = { .ack = &cli->ack_ctx, .op = BT_MESH_SCENE_OP_REGISTER_STATUS, .user_data = rsp, .timeout = model_ackd_timeout_get(cli->model, ctx), }; return bt_mesh_msg_ackd_send(cli->model, ctx, &buf, rsp ? &rsp_ctx : NULL); } int bt_mesh_scene_cli_store(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, uint16_t scene, struct bt_mesh_scene_register *rsp) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_STORE, BT_MESH_SCENE_MSG_LEN_STORE); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_STORE); if (scene == BT_MESH_SCENE_NONE) { return -EINVAL; } net_buf_simple_add_le16(&buf, scene); struct bt_mesh_msg_rsp_ctx rsp_ctx = { .ack = &cli->ack_ctx, .op = BT_MESH_SCENE_OP_REGISTER_STATUS, .user_data = rsp, .timeout = model_ackd_timeout_get(cli->model, ctx), }; return bt_mesh_msg_ackd_send(cli->model, ctx, &buf, rsp ? &rsp_ctx : NULL); } int bt_mesh_scene_cli_store_unack(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, uint16_t scene) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_STORE_UNACK, BT_MESH_SCENE_MSG_LEN_STORE); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_STORE_UNACK); if (scene == BT_MESH_SCENE_NONE) { return -EINVAL; } net_buf_simple_add_le16(&buf, scene); return bt_mesh_msg_send(cli->model, ctx, &buf); } int bt_mesh_scene_cli_delete(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, uint16_t scene, struct bt_mesh_scene_register *rsp) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_DELETE, BT_MESH_SCENE_MSG_LEN_DELETE); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_DELETE); if (scene == BT_MESH_SCENE_NONE) { return -EINVAL; } net_buf_simple_add_le16(&buf, scene); struct bt_mesh_msg_rsp_ctx rsp_ctx = { .ack = &cli->ack_ctx, .op = BT_MESH_SCENE_OP_REGISTER_STATUS, .user_data = rsp, .timeout = model_ackd_timeout_get(cli->model, ctx), }; return bt_mesh_msg_ackd_send(cli->model, ctx, &buf, rsp ? &rsp_ctx : NULL); } int bt_mesh_scene_cli_delete_unack(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, uint16_t scene) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_DELETE_UNACK, BT_MESH_SCENE_MSG_LEN_DELETE); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_DELETE_UNACK); if (scene == BT_MESH_SCENE_NONE) { return -EINVAL; } net_buf_simple_add_le16(&buf, scene); return bt_mesh_msg_send(cli->model, ctx, &buf); } int bt_mesh_scene_cli_recall(struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, uint16_t scene, const struct bt_mesh_model_transition *transition, struct bt_mesh_scene_state *rsp) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_RECALL, BT_MESH_SCENE_MSG_MAXLEN_RECALL); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_RECALL); if (scene == BT_MESH_SCENE_NONE || model_transition_is_invalid(transition)) { return -EINVAL; } net_buf_simple_add_le16(&buf, scene); net_buf_simple_add_u8(&buf, cli->tid++); if (transition) { model_transition_buf_add(&buf, transition); } struct bt_mesh_msg_rsp_ctx rsp_ctx = { .ack = &cli->ack_ctx, .op = BT_MESH_SCENE_OP_STATUS, .user_data = rsp, .timeout = model_ackd_timeout_get(cli->model, ctx), }; return bt_mesh_msg_ackd_send(cli->model, ctx, &buf, rsp ? &rsp_ctx : NULL); } int bt_mesh_scene_cli_recall_unack( struct bt_mesh_scene_cli *cli, struct bt_mesh_msg_ctx *ctx, uint16_t scene, const struct bt_mesh_model_transition *transition) { BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_RECALL_UNACK, BT_MESH_SCENE_MSG_MAXLEN_RECALL); bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_RECALL_UNACK); if (scene == BT_MESH_SCENE_NONE || model_transition_is_invalid(transition)) { return -EINVAL; } net_buf_simple_add_le16(&buf, scene); net_buf_simple_add_u8(&buf, cli->tid++); if (transition) { model_transition_buf_add(&buf, transition); } return bt_mesh_msg_send(cli->model, ctx, &buf); } #endif /* CONFIG_BT_MESH_SCENE_CLI */