/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models * * Copyright (c) 2018 Vikrant More * * SPDX-License-Identifier: Apache-2.0 */ #include "console/console.h" #include "hal/hal_gpio.h" #include "mesh/mesh.h" #include "app_gpio.h" #include "storage.h" #include "ble_mesh.h" #include "device_composition.h" #include "state_binding.h" #include "transition.h" static struct bt_mesh_health_srv health_srv = { }; static struct bt_mesh_model_pub health_pub; static struct bt_mesh_model_pub gen_onoff_srv_pub_root; static struct bt_mesh_model_pub gen_onoff_cli_pub_root; static struct bt_mesh_model_pub gen_level_srv_pub_root; static struct bt_mesh_model_pub gen_level_cli_pub_root; static struct bt_mesh_model_pub gen_def_trans_time_srv_pub; static struct bt_mesh_model_pub gen_def_trans_time_cli_pub; static struct bt_mesh_model_pub gen_power_onoff_srv_pub; static struct bt_mesh_model_pub gen_power_onoff_cli_pub; static struct bt_mesh_model_pub light_lightness_srv_pub; static struct bt_mesh_model_pub light_lightness_cli_pub; static struct bt_mesh_model_pub light_ctl_srv_pub; static struct bt_mesh_model_pub light_ctl_cli_pub; static struct bt_mesh_model_pub vnd_pub; static struct bt_mesh_model_pub gen_level_srv_pub_s0; static struct bt_mesh_model_pub gen_level_cli_pub_s0; static struct os_mbuf *bt_mesh_pub_msg_health_pub; static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_srv_pub_root; static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_cli_pub_root; static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_root; static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_root; static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_srv_pub; static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_cli_pub; static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_srv_pub; static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_cli_pub; static struct os_mbuf *bt_mesh_pub_msg_light_lightness_srv_pub; static struct os_mbuf *bt_mesh_pub_msg_light_lightness_cli_pub; static struct os_mbuf *bt_mesh_pub_msg_light_ctl_srv_pub; static struct os_mbuf *bt_mesh_pub_msg_light_ctl_cli_pub; static struct os_mbuf *bt_mesh_pub_msg_vnd_pub; static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_s0; static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_s0; void init_pub(void) { bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0); bt_mesh_pub_msg_gen_onoff_srv_pub_root = NET_BUF_SIMPLE(2 + 3); bt_mesh_pub_msg_gen_onoff_cli_pub_root = NET_BUF_SIMPLE(2 + 4); bt_mesh_pub_msg_gen_level_srv_pub_root = NET_BUF_SIMPLE(2 + 5); bt_mesh_pub_msg_gen_level_cli_pub_root = NET_BUF_SIMPLE(2 + 7); bt_mesh_pub_msg_gen_power_onoff_srv_pub = NET_BUF_SIMPLE(2 + 1); bt_mesh_pub_msg_gen_power_onoff_cli_pub = NET_BUF_SIMPLE(2 + 1); bt_mesh_pub_msg_gen_def_trans_time_srv_pub = NET_BUF_SIMPLE(2 + 1); bt_mesh_pub_msg_gen_def_trans_time_cli_pub = NET_BUF_SIMPLE(2 + 1); bt_mesh_pub_msg_light_lightness_srv_pub = NET_BUF_SIMPLE(2 + 5); bt_mesh_pub_msg_light_lightness_cli_pub = NET_BUF_SIMPLE(2 + 5); bt_mesh_pub_msg_light_ctl_srv_pub = NET_BUF_SIMPLE(2 + 9); bt_mesh_pub_msg_light_ctl_cli_pub = NET_BUF_SIMPLE(2 + 9); bt_mesh_pub_msg_vnd_pub = NET_BUF_SIMPLE(3 + 6); bt_mesh_pub_msg_gen_level_srv_pub_s0 = NET_BUF_SIMPLE(2 + 5); bt_mesh_pub_msg_gen_level_cli_pub_s0 = NET_BUF_SIMPLE(2 + 7); health_pub.msg = bt_mesh_pub_msg_health_pub; gen_onoff_srv_pub_root.msg = bt_mesh_pub_msg_gen_onoff_srv_pub_root; gen_onoff_cli_pub_root.msg = bt_mesh_pub_msg_gen_onoff_cli_pub_root; gen_level_srv_pub_root.msg = bt_mesh_pub_msg_gen_level_srv_pub_root; gen_level_cli_pub_root.msg = bt_mesh_pub_msg_gen_level_cli_pub_root; gen_power_onoff_srv_pub.msg = bt_mesh_pub_msg_gen_power_onoff_srv_pub; gen_power_onoff_cli_pub.msg = bt_mesh_pub_msg_gen_power_onoff_cli_pub; gen_def_trans_time_srv_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_srv_pub; gen_def_trans_time_cli_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_cli_pub; light_lightness_srv_pub.msg = bt_mesh_pub_msg_light_lightness_srv_pub; light_lightness_cli_pub.msg = bt_mesh_pub_msg_light_lightness_cli_pub; light_ctl_srv_pub.msg = bt_mesh_pub_msg_light_ctl_srv_pub; light_ctl_cli_pub.msg = bt_mesh_pub_msg_light_ctl_cli_pub; vnd_pub.msg = bt_mesh_pub_msg_vnd_pub; gen_level_srv_pub_s0.msg = bt_mesh_pub_msg_gen_level_srv_pub_s0; gen_level_cli_pub_s0.msg = bt_mesh_pub_msg_gen_level_cli_pub_s0; } /* Definitions of models user data (Start) */ struct generic_onoff_state gen_onoff_srv_root_user_data = { .transition = &lightness_transition, }; struct generic_level_state gen_level_srv_root_user_data = { .transition = &lightness_transition, }; struct gen_def_trans_time_state gen_def_trans_time_srv_user_data; struct generic_onpowerup_state gen_power_onoff_srv_user_data; struct light_lightness_state light_lightness_srv_user_data = { .transition = &lightness_transition, }; struct light_ctl_state light_ctl_srv_user_data = { .transition = &lightness_transition, }; struct vendor_state vnd_user_data; struct generic_level_state gen_level_srv_s0_user_data = { .transition = &temp_transition, }; /* Definitions of models user data (End) */ static struct bt_mesh_elem elements[]; /* message handlers (Start) */ /* Generic OnOff Server message handlers */ static int gen_onoff_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); struct generic_onoff_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); net_buf_simple_add_u8(msg, state->onoff); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_u8(msg, state->target_onoff); net_buf_simple_add_u8(msg, state->transition->rt); } if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send GEN_ONOFF_SRV Status response\n"); } os_mbuf_free_chain(msg); return 0; } int gen_onoff_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct generic_onoff_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); net_buf_simple_add_u8(msg, state->onoff); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_u8(msg, state->target_onoff); net_buf_simple_add_u8(msg, state->transition->rt); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int gen_onoff_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, onoff, tt, delay; int64_t now; int err; struct generic_onoff_state *state = model->user_data; onoff = net_buf_simple_pull_u8(buf); tid = net_buf_simple_pull_u8(buf); if (onoff > STATE_ON) { return 0; } now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_onoff = onoff; if (state->target_onoff != state->onoff) { onoff_tt_values(state, tt, delay); } else { return gen_onoff_publish(model); } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->onoff = state->target_onoff; } state->transition->just_started = true; err = gen_onoff_publish(model); onoff_handler(state); if (err) { return err; } return 0; } static int gen_onoff_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, onoff, tt, delay; int64_t now; int rc; struct generic_onoff_state *state = model->user_data; onoff = net_buf_simple_pull_u8(buf); tid = net_buf_simple_pull_u8(buf); if (onoff > STATE_ON) { return 0; } now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { rc = gen_onoff_get(model, ctx, buf); return rc; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_onoff = onoff; if (state->target_onoff != state->onoff) { onoff_tt_values(state, tt, delay); } else { gen_onoff_get(model, ctx, buf); rc = gen_onoff_publish(model); return rc; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->onoff = state->target_onoff; } state->transition->just_started = true; gen_onoff_get(model, ctx, buf); rc = gen_onoff_publish(model); onoff_handler(state); return rc; } /* Generic OnOff Client message handlers */ static int gen_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from GEN_ONOFF_SRV\n"); printk("Present OnOff = %02x\n", net_buf_simple_pull_u8(buf)); if (buf->om_len == 2) { printk("Target OnOff = %02x\n", net_buf_simple_pull_u8(buf)); printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); } return 0; } /* Generic Level Server message handlers */ static int gen_level_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); struct generic_level_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); net_buf_simple_add_le16(msg, state->level); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_level); net_buf_simple_add_u8(msg, state->transition->rt); } if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send GEN_LEVEL_SRV Status response\n"); } os_mbuf_free_chain(msg); return 0; } int gen_level_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct generic_level_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); net_buf_simple_add_le16(msg, state->level); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_level); net_buf_simple_add_u8(msg, state->transition->rt); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int gen_level_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t level; int64_t now; struct generic_level_state *state = model->user_data; level = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_level = level; if (state->target_level != state->level) { level_tt_values(state, tt, delay); } else { gen_level_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->level = state->target_level; } state->transition->just_started = true; gen_level_publish(model); if (bt_mesh_model_elem(model)->addr == elements[0].addr) { /* Root element */ transition_type = LEVEL_TT; level_lightness_handler(state); } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { /* Secondary element */ transition_type = LEVEL_TEMP_TT; level_temp_handler(state); } return 0; } static int gen_level_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t level; int64_t now; struct generic_level_state *state = model->user_data; level = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { gen_level_get(model, ctx, buf); return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_level = level; if (state->target_level != state->level) { level_tt_values(state, tt, delay); } else { gen_level_get(model, ctx, buf); gen_level_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->level = state->target_level; } state->transition->just_started = true; gen_level_get(model, ctx, buf); gen_level_publish(model); if (bt_mesh_model_elem(model)->addr == elements[0].addr) { /* Root element */ transition_type = LEVEL_TT; level_lightness_handler(state); } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { /* Secondary element */ transition_type = LEVEL_TEMP_TT; level_temp_handler(state); } return 0; } static int gen_delta_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int32_t tmp32, delta; int64_t now; struct generic_level_state *state = model->user_data; delta = (int32_t) net_buf_simple_pull_le32(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { if (state->last_delta == delta) { return 0; } tmp32 = state->last_level + delta; } else { state->last_level = state->level; tmp32 = state->level + delta; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->last_delta = delta; if (tmp32 < INT16_MIN) { tmp32 = INT16_MIN; } else if (tmp32 > INT16_MAX) { tmp32 = INT16_MAX; } state->target_level = tmp32; if (state->target_level != state->level) { level_tt_values(state, tt, delay); } else { return gen_level_publish(model); } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->level = state->target_level; } state->transition->just_started = true; gen_level_publish(model); if (bt_mesh_model_elem(model)->addr == elements[0].addr) { /* Root element */ transition_type = LEVEL_TT_DELTA; level_lightness_handler(state); } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { /* Secondary element */ transition_type = LEVEL_TEMP_TT_DELTA; level_temp_handler(state); } return 0; } static int gen_delta_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int32_t tmp32, delta; int64_t now; struct generic_level_state *state = model->user_data; delta = (int32_t) net_buf_simple_pull_le32(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { if (state->last_delta == delta) { gen_level_get(model, ctx, buf); return 0; } tmp32 = state->last_level + delta; } else { state->last_level = state->level; tmp32 = state->level + delta; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->last_delta = delta; if (tmp32 < INT16_MIN) { tmp32 = INT16_MIN; } else if (tmp32 > INT16_MAX) { tmp32 = INT16_MAX; } state->target_level = tmp32; if (state->target_level != state->level) { level_tt_values(state, tt, delay); } else { gen_level_get(model, ctx, buf); gen_level_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->level = state->target_level; } state->transition->just_started = true; gen_level_get(model, ctx, buf); gen_level_publish(model); if (bt_mesh_model_elem(model)->addr == elements[0].addr) { /* Root element */ transition_type = LEVEL_TT_DELTA; level_lightness_handler(state); } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { /* Secondary element */ transition_type = LEVEL_TEMP_TT_DELTA; level_temp_handler(state); } return 0; } static int gen_level_move_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); struct generic_level_state *state = model->user_data; int rc; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); net_buf_simple_add_le16(msg, state->level); if (state->transition->counter) { if (state->last_delta < 0) { net_buf_simple_add_le16(msg, INT16_MIN); } else { /* 0 should not be possible */ net_buf_simple_add_le16(msg, INT16_MAX); } net_buf_simple_add_u8(msg, UNKNOWN_VALUE); } rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); if (rc) { printk("Unable to send GEN_LEVEL_SRV Status response\n"); } os_mbuf_free_chain(msg); return rc; } static int gen_level_move_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct generic_level_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); net_buf_simple_add_le16(msg, state->level); if (state->transition->counter) { if (state->last_delta < 0) { net_buf_simple_add_le16(msg, INT16_MIN); } else { /* 0 should not be possible */ net_buf_simple_add_le16(msg, INT16_MAX); } net_buf_simple_add_u8(msg, UNKNOWN_VALUE); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int gen_move_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t delta; int32_t tmp32; int64_t now; struct generic_level_state *state = model->user_data; int rc; delta = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->last_delta = delta; tmp32 = state->level + delta; if (tmp32 < INT16_MIN) { tmp32 = INT16_MIN; } else if (tmp32 > INT16_MAX) { tmp32 = INT16_MAX; } state->target_level = tmp32; if (state->target_level != state->level) { level_tt_values(state, tt, delay); } else { rc = gen_level_move_publish(model); return rc; } if (state->transition->counter == 0) { return 0; } state->transition->just_started = true; rc = gen_level_move_publish(model); if (bt_mesh_model_elem(model)->addr == elements[0].addr) { /* Root element */ transition_type = LEVEL_TT_MOVE; level_lightness_handler(state); } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { /* Secondary element */ transition_type = LEVEL_TEMP_TT_MOVE; level_temp_handler(state); } return rc; } static int gen_move_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t delta; int32_t tmp32; int64_t now; struct generic_level_state *state = model->user_data; int rc; delta = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { rc = gen_level_move_get(model, ctx, buf); return rc; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->last_delta = delta; tmp32 = state->level + delta; if (tmp32 < INT16_MIN) { tmp32 = INT16_MIN; } else if (tmp32 > INT16_MAX) { tmp32 = INT16_MAX; } state->target_level = tmp32; if (state->target_level != state->level) { level_tt_values(state, tt, delay); } else { gen_level_move_get(model, ctx, buf); rc = gen_level_move_publish(model); return rc; } if (state->transition->counter == 0) { return 0; } state->transition->just_started = true; gen_level_move_get(model, ctx, buf); rc = gen_level_move_publish(model); if (bt_mesh_model_elem(model)->addr == elements[0].addr) { /* Root element */ transition_type = LEVEL_TT_MOVE; level_lightness_handler(state); } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { /* Secondary element */ transition_type = LEVEL_TEMP_TT_MOVE; level_temp_handler(state); } return rc; } /* Generic Level Client message handlers */ static int gen_level_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from GEN_LEVEL_SRV\n"); printk("Present Level = %04x\n", net_buf_simple_pull_le16(buf)); if (buf->om_len == 3) { printk("Target Level = %04x\n", net_buf_simple_pull_le16(buf)); printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); } return 0; } /* Generic Default Transition Time Server message handlers */ static int gen_def_trans_time_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); struct gen_def_trans_time_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS); net_buf_simple_add_u8(msg, state->tt); if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send GEN_DEF_TT_SRV Status response\n"); } os_mbuf_free_chain(msg); return 0; } static int gen_def_trans_time_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct gen_def_trans_time_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS); net_buf_simple_add_u8(msg, state->tt); err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static bool gen_def_trans_time_setunack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tt; struct gen_def_trans_time_state *state = model->user_data; tt = net_buf_simple_pull_u8(buf); /* Here, Model specification is silent about tid implementation */ if ((tt & 0x3F) == 0x3F) { return false; } if (state->tt != tt) { state->tt = tt; default_tt = tt; save_on_flash(GEN_DEF_TRANS_TIME_STATE); } return true; } static int gen_def_trans_time_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (gen_def_trans_time_setunack(model, ctx, buf) == true) { return gen_def_trans_time_publish(model); } return 0; } static int gen_def_trans_time_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (gen_def_trans_time_setunack(model, ctx, buf) == true) { gen_def_trans_time_get(model, ctx, buf); return gen_def_trans_time_publish(model); } return 0; } /* Generic Default Transition Time Client message handlers */ static int gen_def_trans_time_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from GEN_DEF_TT_SRV\n"); printk("Transition Time = %02x\n", net_buf_simple_pull_u8(buf)); return 0; } /* Generic Power OnOff Server message handlers */ static int gen_onpowerup_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); struct generic_onpowerup_state *state = model->user_data; int rc; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS); net_buf_simple_add_u8(msg, state->onpowerup); rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); if (rc) { printk("Unable to send GEN_POWER_ONOFF_SRV Status response\n"); } os_mbuf_free_chain(msg); return rc; } /* Generic Power OnOff Client message handlers */ static int gen_onpowerup_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from GEN_POWER_ONOFF_SRV\n"); printk("OnPowerUp = %02x\n", net_buf_simple_pull_u8(buf)); return 0; } /* Generic Power OnOff Setup Server message handlers */ static int gen_onpowerup_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct generic_onpowerup_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS); net_buf_simple_add_u8(msg, state->onpowerup); err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static bool gen_onpowerup_setunack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t onpowerup; struct generic_onpowerup_state *state = model->user_data; onpowerup = net_buf_simple_pull_u8(buf); /* Here, Model specification is silent about tid implementation */ if (onpowerup > STATE_RESTORE) { return false; } if (state->onpowerup != onpowerup) { state->onpowerup = onpowerup; save_on_flash(GEN_ONPOWERUP_STATE); } return true; } static int gen_onpowerup_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (gen_onpowerup_setunack(model, ctx, buf) == true) { return gen_onpowerup_publish(model); } return 0; } static int gen_onpowerup_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (gen_onpowerup_setunack(model, ctx, buf) == true) { gen_onpowerup_get(model, ctx, buf); return gen_onpowerup_publish(model); } return 0; } /* Vendor Model message handlers*/ static int vnd_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 6 + 4); struct vendor_state *state = model->user_data; int err; /* This is dummy response for demo purpose */ state->response = 0xA578FEB3; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME)); net_buf_simple_add_le16(msg, state->current); net_buf_simple_add_le32(msg, state->response); err = bt_mesh_model_send(model, ctx, msg, NULL, NULL); if (err) { printk("Unable to send VENDOR Status response\n"); } os_mbuf_free_chain(msg); return err; } static int vnd_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid; int current; int64_t now; struct vendor_state *state = model->user_data; current = net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->current = current; printk("Vendor model message = %04x\n", state->current); if (state->current == STATE_ON) { /* LED2 On */ hal_gpio_write(led_device[1], 0); } else { /* LED2 Off */ hal_gpio_write(led_device[1], 1); } return 0; } static int vnd_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { int rc; rc = vnd_set_unack(model, ctx, buf); if (rc) { return rc; } rc = vnd_get(model, ctx, buf); if (rc) { return rc; } return 0; } static int vnd_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from Vendor\n"); printk("cmd = %04x\n", net_buf_simple_pull_le16(buf)); printk("response = %08lx\n", net_buf_simple_pull_le32(buf)); return 0; } /* Light Lightness Server message handlers */ static int light_lightness_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); struct light_lightness_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS); net_buf_simple_add_le16(msg, state->actual); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_actual); net_buf_simple_add_u8(msg, state->transition->rt); } if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightLightnessAct Status response\n"); } os_mbuf_free_chain(msg); return 0; } int light_lightness_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_lightness_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS); net_buf_simple_add_le16(msg, state->actual); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_actual); net_buf_simple_add_u8(msg, state->transition->rt); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int light_lightness_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; uint16_t actual; int64_t now; struct light_lightness_state *state = model->user_data; actual = net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; if (actual > 0 && actual < state->light_range_min) { actual = state->light_range_min; } else if (actual > state->light_range_max) { actual = state->light_range_max; } state->target_actual = actual; if (state->target_actual != state->actual) { light_lightness_actual_tt_values(state, tt, delay); } else { light_lightness_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->actual = state->target_actual; } state->transition->just_started = true; light_lightness_publish(model); light_lightness_actual_handler(state); return 0; } static int light_lightness_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; uint16_t actual; int64_t now; struct light_lightness_state *state = model->user_data; int rc; actual = net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return light_lightness_get(model, ctx, buf); } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; if (actual > 0 && actual < state->light_range_min) { actual = state->light_range_min; } else if (actual > state->light_range_max) { actual = state->light_range_max; } state->target_actual = actual; if (state->target_actual != state->actual) { light_lightness_actual_tt_values(state, tt, delay); } else { rc = light_lightness_get(model, ctx, buf); light_lightness_publish(model); return rc; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->actual = state->target_actual; } state->transition->just_started = true; light_lightness_get(model, ctx, buf); light_lightness_publish(model); light_lightness_actual_handler(state); return 0; } static int light_lightness_linear_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); struct light_lightness_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS); net_buf_simple_add_le16(msg, state->linear); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_linear); net_buf_simple_add_u8(msg, state->transition->rt); } if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightLightnessLin Status response\n"); } os_mbuf_free_chain(msg); return 0; } int light_lightness_linear_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_lightness_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS); net_buf_simple_add_le16(msg, state->linear); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_linear); net_buf_simple_add_u8(msg, state->transition->rt); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int light_lightness_linear_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; uint16_t linear; int64_t now; struct light_lightness_state *state = model->user_data; linear = net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_linear = linear; if (state->target_linear != state->linear) { light_lightness_linear_tt_values(state, tt, delay); } else { light_lightness_linear_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->linear = state->target_linear; } state->transition->just_started = true; light_lightness_linear_publish(model); light_lightness_linear_handler(state); return 0; } static int light_lightness_linear_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; uint16_t linear; int64_t now; struct light_lightness_state *state = model->user_data; int rc; linear = net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return light_lightness_linear_get(model, ctx, buf); } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_linear = linear; if (state->target_linear != state->linear) { light_lightness_linear_tt_values(state, tt, delay); } else { rc = light_lightness_linear_get(model, ctx, buf); light_lightness_linear_publish(model); return rc; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->linear = state->target_linear; } state->transition->just_started = true; light_lightness_linear_get(model, ctx, buf); light_lightness_linear_publish(model); light_lightness_linear_handler(state); return 0; } static int light_lightness_last_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); struct light_lightness_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS); net_buf_simple_add_le16(msg, state->last); if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightLightnessLast Status response\n"); } os_mbuf_free_chain(msg); return 0; } static int light_lightness_default_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); struct light_lightness_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS); net_buf_simple_add_le16(msg, state->def); if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightLightnessDef Status response\n"); } os_mbuf_free_chain(msg); return 0; } static int light_lightness_range_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); struct light_lightness_state *state = model->user_data; state->status_code = RANGE_SUCCESSFULLY_UPDATED; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS); net_buf_simple_add_u8(msg, state->status_code); net_buf_simple_add_le16(msg, state->light_range_min); net_buf_simple_add_le16(msg, state->light_range_max); if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightLightnessRange Status response\n"); } os_mbuf_free_chain(msg); return 0; } /* Light Lightness Setup Server message handlers */ static int light_lightness_default_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_lightness_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS); net_buf_simple_add_le16(msg, state->def); err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int light_lightness_default_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint16_t lightness; struct light_lightness_state *state = model->user_data; lightness = net_buf_simple_pull_le16(buf); /* Here, Model specification is silent about tid implementation */ if (state->def != lightness) { state->def = lightness; light_ctl_srv_user_data.lightness_def = state->def; save_on_flash(LIGHTNESS_TEMP_DEF_STATE); } return light_lightness_default_publish(model); } static int light_lightness_default_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { int rc; rc = light_lightness_default_set_unack(model, ctx, buf); if (rc) { return rc; } rc = light_lightness_default_get(model, ctx, buf); if (rc) { return rc; } rc = light_lightness_default_publish(model); if (rc) { return rc; } return 0; } static int light_lightness_range_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_lightness_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS); net_buf_simple_add_u8(msg, state->status_code); net_buf_simple_add_le16(msg, state->light_range_min); net_buf_simple_add_le16(msg, state->light_range_max); err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static bool light_lightness_range_setunack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint16_t min, max; struct light_lightness_state *state = model->user_data; min = net_buf_simple_pull_le16(buf); max = net_buf_simple_pull_le16(buf); /* Here, Model specification is silent about tid implementation */ if (min == 0 || max == 0) { return false; } else { if (min <= max) { state->status_code = RANGE_SUCCESSFULLY_UPDATED; if (state->light_range_min != min || state->light_range_max != max) { state->light_range_min = min; state->light_range_max = max; save_on_flash(LIGHTNESS_RANGE); } } else { /* The provided value for Range Max cannot be set */ state->status_code = CANNOT_SET_RANGE_MAX; return false; } } return true; } static int light_lightness_range_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (light_lightness_range_setunack(model, ctx, buf) == true) { return light_lightness_range_publish(model); } return 0; } static int light_lightness_range_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { int rc; if (light_lightness_range_setunack(model, ctx, buf) == true) { rc = light_lightness_range_get(model, ctx, buf); if (rc) { return rc; } rc = light_lightness_range_publish(model); if (rc) { return rc; } } return 0; } /* Light Lightness Client message handlers */ static int light_lightness_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Actual)\n"); printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf)); if (buf->om_len == 3) { printk("Target Lightness = %04x\n", net_buf_simple_pull_le16(buf)); printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); } return 0; } static int light_lightness_linear_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Linear)\n"); printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf)); if (buf->om_len == 3) { printk("Target Lightness = %04x\n", net_buf_simple_pull_le16(buf)); printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); } return 0; } static int light_lightness_last_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Last)\n"); printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); return 0; } static int light_lightness_default_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Default)\n"); printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); return 0; } static int light_lightness_range_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Lightness Range)\n"); printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf)); printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf)); printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf)); return 0; } /* Light CTL Server message handlers */ static int light_ctl_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); struct light_ctl_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS); net_buf_simple_add_le16(msg, state->lightness); net_buf_simple_add_le16(msg, state->temp); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_lightness); net_buf_simple_add_le16(msg, state->target_temp); net_buf_simple_add_u8(msg, state->transition->rt); } if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightCTL Status response\n"); } os_mbuf_free_chain(msg); return 0; } int light_ctl_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_ctl_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS); /* Here, as per Model specification, status should be * made up of lightness & temperature values only */ net_buf_simple_add_le16(msg, state->lightness); net_buf_simple_add_le16(msg, state->temp); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_lightness); net_buf_simple_add_le16(msg, state->target_temp); net_buf_simple_add_u8(msg, state->transition->rt); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int light_ctl_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t delta_uv; uint16_t lightness, temp; int64_t now; struct light_ctl_state *state = model->user_data; lightness = net_buf_simple_pull_le16(buf); temp = net_buf_simple_pull_le16(buf); delta_uv = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); if (temp < TEMP_MIN || temp > TEMP_MAX) { return 0; } now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_lightness = lightness; if (temp < state->temp_range_min) { temp = state->temp_range_min; } else if (temp > state->temp_range_max) { temp = state->temp_range_max; } state->target_temp = temp; state->target_delta_uv = delta_uv; if (state->target_lightness != state->lightness || state->target_temp != state->temp || state->target_delta_uv != state->delta_uv) { light_ctl_tt_values(state, tt, delay); } else { light_ctl_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->lightness = state->target_lightness; state->temp = state->target_temp; state->delta_uv = state->target_delta_uv; } state->transition->just_started = true; light_ctl_publish(model); light_ctl_handler(state); return 0; } static int light_ctl_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t delta_uv; uint16_t lightness, temp; int64_t now; struct light_ctl_state *state = model->user_data; lightness = net_buf_simple_pull_le16(buf); temp = net_buf_simple_pull_le16(buf); delta_uv = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); if (temp < TEMP_MIN || temp > TEMP_MAX) { return 0; } now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { light_ctl_get(model, ctx, buf); return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; state->target_lightness = lightness; if (temp < state->temp_range_min) { temp = state->temp_range_min; } else if (temp > state->temp_range_max) { temp = state->temp_range_max; } state->target_temp = temp; state->target_delta_uv = delta_uv; if (state->target_lightness != state->lightness || state->target_temp != state->temp || state->target_delta_uv != state->delta_uv) { light_ctl_tt_values(state, tt, delay); } else { light_ctl_get(model, ctx, buf); light_ctl_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->lightness = state->target_lightness; state->temp = state->target_temp; state->delta_uv = state->target_delta_uv; } state->transition->just_started = true; light_ctl_get(model, ctx, buf); light_ctl_publish(model); light_ctl_handler(state); return 0; } static int light_ctl_temp_range_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); struct light_ctl_state *state = model->user_data; int rc; state->status_code = RANGE_SUCCESSFULLY_UPDATED; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS); net_buf_simple_add_u8(msg, state->status_code); net_buf_simple_add_le16(msg, state->temp_range_min); net_buf_simple_add_le16(msg, state->temp_range_max); rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); if (rc) { printk("Unable to send LightCTL Temp Range Status response\n"); } os_mbuf_free_chain(msg); return rc; } static int light_ctl_default_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 6 + 4); struct light_ctl_state *state = model->user_data; int rc; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS); net_buf_simple_add_le16(msg, state->lightness_def); net_buf_simple_add_le16(msg, state->temp_def); net_buf_simple_add_le16(msg, state->delta_uv_def); rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL); if (rc) { printk("Unable to send LightCTL Default Status response\n"); } os_mbuf_free_chain(msg); return rc; } /* Light CTL Setup Server message handlers */ static int light_ctl_default_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_ctl_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS); net_buf_simple_add_le16(msg, state->lightness_def); net_buf_simple_add_le16(msg, state->temp_def); net_buf_simple_add_le16(msg, state->delta_uv_def); err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static bool light_ctl_default_setunack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint16_t lightness, temp; int16_t delta_uv; struct light_ctl_state *state = model->user_data; lightness = net_buf_simple_pull_le16(buf); temp = net_buf_simple_pull_le16(buf); delta_uv = (int16_t) net_buf_simple_pull_le16(buf); /* Here, Model specification is silent about tid implementation */ if (temp < TEMP_MIN || temp > TEMP_MAX) { return false; } if (temp < state->temp_range_min) { temp = state->temp_range_min; } else if (temp > state->temp_range_max) { temp = state->temp_range_max; } if (state->lightness_def != lightness || state->temp_def != temp || state->delta_uv_def != delta_uv) { state->lightness_def = lightness; state->temp_def = temp; state->delta_uv_def = delta_uv; save_on_flash(LIGHTNESS_TEMP_DEF_STATE); } return true; } static int light_ctl_default_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (light_ctl_default_setunack(model, ctx, buf) == true) { return light_ctl_default_publish(model); } return 0; } static int light_ctl_default_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (light_ctl_default_setunack(model, ctx, buf) == true) { light_ctl_default_get(model, ctx, buf); return light_ctl_default_publish(model); } return 0; } static int light_ctl_temp_range_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_ctl_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS); net_buf_simple_add_u8(msg, state->status_code); net_buf_simple_add_le16(msg, state->temp_range_min); net_buf_simple_add_le16(msg, state->temp_range_max); err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static bool light_ctl_temp_range_setunack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint16_t min, max; struct light_ctl_state *state = model->user_data; min = net_buf_simple_pull_le16(buf); max = net_buf_simple_pull_le16(buf); /* Here, Model specification is silent about tid implementation */ /* This is as per 6.1.3.1 in Mesh Model Specification */ if (min < TEMP_MIN || min > TEMP_MAX || max < TEMP_MIN || max > TEMP_MAX) { return false; } if (min <= max) { state->status_code = RANGE_SUCCESSFULLY_UPDATED; if (state->temp_range_min != min || state->temp_range_max != max) { state->temp_range_min = min; state->temp_range_max = max; save_on_flash(TEMPERATURE_RANGE); } } else { /* The provided value for Range Max cannot be set */ state->status_code = CANNOT_SET_RANGE_MAX; return false; } return true; } static int light_ctl_temp_range_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (light_ctl_temp_range_setunack(model, ctx, buf) == true) { return light_ctl_temp_range_publish(model); } return 0; } static int light_ctl_temp_range_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { if (light_ctl_temp_range_setunack(model, ctx, buf) == true) { light_ctl_temp_range_get(model, ctx, buf); return light_ctl_temp_range_publish(model); } return 0; } /* Light CTL Client message handlers */ static int light_ctl_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_CTL_SRV\n"); printk("Present CTL Lightness = %04x\n", net_buf_simple_pull_le16(buf)); printk("Present CTL Temperature = %04x\n", net_buf_simple_pull_le16(buf)); if (buf->om_len == 5) { printk("Target CTL Lightness = %04x\n", net_buf_simple_pull_le16(buf)); printk("Target CTL Temperature = %04x\n", net_buf_simple_pull_le16(buf)); printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); } return 0; } static int light_ctl_temp_range_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_CTL_SRV (Temperature Range)\n"); printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf)); printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf)); printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf)); return 0; } static int light_ctl_temp_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_CTL_TEMP_SRV\n"); printk("Present CTL Temperature = %04x\n", net_buf_simple_pull_le16(buf)); printk("Present CTL Delta UV = %04x\n", net_buf_simple_pull_le16(buf)); if (buf->om_len == 5) { printk("Target CTL Temperature = %04x\n", net_buf_simple_pull_le16(buf)); printk("Target CTL Delta UV = %04x\n", net_buf_simple_pull_le16(buf)); printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); } return 0; } static int light_ctl_default_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { printk("Acknownledgement from LIGHT_CTL_SRV (Default)\n"); printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); printk("Temperature = %04x\n", net_buf_simple_pull_le16(buf)); printk("Delta UV = %04x\n", net_buf_simple_pull_le16(buf)); return 0; } /* Light CTL Temp. Server message handlers */ static int light_ctl_temp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); struct light_ctl_state *state = model->user_data; bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS); net_buf_simple_add_le16(msg, state->temp); net_buf_simple_add_le16(msg, state->delta_uv); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_temp); net_buf_simple_add_le16(msg, state->target_delta_uv); net_buf_simple_add_u8(msg, state->transition->rt); } if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { printk("Unable to send LightCTL Temp. Status response\n"); } os_mbuf_free_chain(msg); return 0; } int light_ctl_temp_publish(struct bt_mesh_model *model) { int err; struct os_mbuf *msg = model->pub->msg; struct light_ctl_state *state = model->user_data; if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { return 0; } bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS); net_buf_simple_add_le16(msg, state->temp); net_buf_simple_add_le16(msg, state->delta_uv); if (state->transition->counter) { calculate_rt(state->transition); net_buf_simple_add_le16(msg, state->target_temp); net_buf_simple_add_le16(msg, state->target_delta_uv); net_buf_simple_add_u8(msg, state->transition->rt); } err = bt_mesh_model_publish(model); if (err) { printk("bt_mesh_model_publish err %d\n", err); } return err; } static int light_ctl_temp_set_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t delta_uv; uint16_t temp; int64_t now; struct light_ctl_state *state = model->user_data; temp = net_buf_simple_pull_le16(buf); delta_uv = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); if (temp < TEMP_MIN || temp > TEMP_MAX) { return 0; } now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return 0; } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; if (temp < state->temp_range_min) { temp = state->temp_range_min; } else if (temp > state->temp_range_max) { temp = state->temp_range_max; } state->target_temp = temp; state->target_delta_uv = delta_uv; if (state->target_temp != state->temp || state->target_delta_uv != state->delta_uv) { light_ctl_temp_tt_values(state, tt, delay); } else { light_ctl_temp_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->temp = state->target_temp; state->delta_uv = state->target_delta_uv; } state->transition->just_started = true; light_ctl_temp_publish(model); light_ctl_temp_handler(state); return 0; } static int light_ctl_temp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf) { uint8_t tid, tt, delay; int16_t delta_uv; uint16_t temp; int64_t now; struct light_ctl_state *state = model->user_data; temp = net_buf_simple_pull_le16(buf); delta_uv = (int16_t) net_buf_simple_pull_le16(buf); tid = net_buf_simple_pull_u8(buf); if (temp < TEMP_MIN || temp > TEMP_MAX) { return 0; } now = k_uptime_get(); if (state->last_tid == tid && state->last_src_addr == ctx->addr && state->last_dst_addr == ctx->recv_dst && (now - state->last_msg_timestamp <= K_SECONDS(6))) { return light_ctl_temp_get(model, ctx, buf); } switch (buf->om_len) { case 0x00: /* No optional fields are available */ tt = default_tt; delay = 0; break; case 0x02: /* Optional fields are available */ tt = net_buf_simple_pull_u8(buf); if ((tt & 0x3F) == 0x3F) { return 0; } delay = net_buf_simple_pull_u8(buf); break; default: return 0; } *ptr_counter = 0; os_callout_stop(ptr_timer); state->last_tid = tid; state->last_src_addr = ctx->addr; state->last_dst_addr = ctx->recv_dst; state->last_msg_timestamp = now; if (temp < state->temp_range_min) { temp = state->temp_range_min; } else if (temp > state->temp_range_max) { temp = state->temp_range_max; } state->target_temp = temp; state->target_delta_uv = delta_uv; if (state->target_temp != state->temp || state->target_delta_uv != state->delta_uv) { light_ctl_temp_tt_values(state, tt, delay); } else { light_ctl_temp_get(model, ctx, buf); light_ctl_temp_publish(model); return 0; } /* For Instantaneous Transition */ if (state->transition->counter == 0) { state->temp = state->target_temp; state->delta_uv = state->target_delta_uv; } state->transition->just_started = true; light_ctl_temp_get(model, ctx, buf); light_ctl_temp_publish(model); light_ctl_temp_handler(state); return 0; } /* message handlers (End) */ /* Mapping of message handlers for Generic OnOff Server (0x1000) */ static const struct bt_mesh_model_op gen_onoff_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get }, { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set }, { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic OnOff Client (0x1001) */ static const struct bt_mesh_model_op gen_onoff_cli_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x04), 1, gen_onoff_status }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Levl Server (0x1002) */ static const struct bt_mesh_model_op gen_level_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get }, { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set }, { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set }, { BT_MESH_MODEL_OP_2(0x82, 0x0A), 5, gen_delta_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x0B), 3, gen_move_set }, { BT_MESH_MODEL_OP_2(0x82, 0x0C), 3, gen_move_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Level Client (0x1003) */ static const struct bt_mesh_model_op gen_level_cli_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x08), 2, gen_level_status }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Default TT Server (0x1004) */ static const struct bt_mesh_model_op gen_def_trans_time_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x0D), 0, gen_def_trans_time_get }, { BT_MESH_MODEL_OP_2(0x82, 0x0E), 1, gen_def_trans_time_set }, { BT_MESH_MODEL_OP_2(0x82, 0x0F), 1, gen_def_trans_time_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Default TT Client (0x1005) */ static const struct bt_mesh_model_op gen_def_trans_time_cli_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x10), 1, gen_def_trans_time_status }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Power OnOff Server (0x1006) */ static const struct bt_mesh_model_op gen_power_onoff_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x11), 0, gen_onpowerup_get }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Power OnOff Setup Server (0x1007) */ static const struct bt_mesh_model_op gen_power_onoff_setup_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x13), 1, gen_onpowerup_set }, { BT_MESH_MODEL_OP_2(0x82, 0x14), 1, gen_onpowerup_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Generic Power OnOff Client (0x1008) */ static const struct bt_mesh_model_op gen_power_onoff_cli_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x12), 1, gen_onpowerup_status }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light Lightness Server (0x1300) */ static const struct bt_mesh_model_op light_lightness_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x4B), 0, light_lightness_get }, { BT_MESH_MODEL_OP_2(0x82, 0x4C), 3, light_lightness_set }, { BT_MESH_MODEL_OP_2(0x82, 0x4D), 3, light_lightness_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x4F), 0, light_lightness_linear_get }, { BT_MESH_MODEL_OP_2(0x82, 0x50), 3, light_lightness_linear_set }, { BT_MESH_MODEL_OP_2(0x82, 0x51), 3, light_lightness_linear_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x53), 0, light_lightness_last_get }, { BT_MESH_MODEL_OP_2(0x82, 0x55), 0, light_lightness_default_get }, { BT_MESH_MODEL_OP_2(0x82, 0x57), 0, light_lightness_range_get }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light Lightness Setup Server (0x1301) */ static const struct bt_mesh_model_op light_lightness_setup_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x59), 2, light_lightness_default_set }, { BT_MESH_MODEL_OP_2(0x82, 0x5A), 2, light_lightness_default_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x5B), 4, light_lightness_range_set }, { BT_MESH_MODEL_OP_2(0x82, 0x5C), 4, light_lightness_range_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light Lightness Client (0x1302) */ static const struct bt_mesh_model_op light_lightness_cli_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x4E), 2, light_lightness_status }, { BT_MESH_MODEL_OP_2(0x82, 0x52), 2, light_lightness_linear_status }, { BT_MESH_MODEL_OP_2(0x82, 0x54), 2, light_lightness_last_status }, { BT_MESH_MODEL_OP_2(0x82, 0x56), 2, light_lightness_default_status }, { BT_MESH_MODEL_OP_2(0x82, 0x58), 5, light_lightness_range_status }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light CTL Server (0x1303) */ static const struct bt_mesh_model_op light_ctl_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x5D), 0, light_ctl_get }, { BT_MESH_MODEL_OP_2(0x82, 0x5E), 7, light_ctl_set }, { BT_MESH_MODEL_OP_2(0x82, 0x5F), 7, light_ctl_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x62), 0, light_ctl_temp_range_get }, { BT_MESH_MODEL_OP_2(0x82, 0x67), 0, light_ctl_default_get }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light CTL Setup Server (0x1304) */ static const struct bt_mesh_model_op light_ctl_setup_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x69), 6, light_ctl_default_set }, { BT_MESH_MODEL_OP_2(0x82, 0x6A), 6, light_ctl_default_set_unack }, { BT_MESH_MODEL_OP_2(0x82, 0x6B), 4, light_ctl_temp_range_set }, { BT_MESH_MODEL_OP_2(0x82, 0x6C), 4, light_ctl_temp_range_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light CTL Client (0x1305) */ static const struct bt_mesh_model_op light_ctl_cli_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x60), 4, light_ctl_status }, { BT_MESH_MODEL_OP_2(0x82, 0x63), 5, light_ctl_temp_range_status }, { BT_MESH_MODEL_OP_2(0x82, 0x66), 4, light_ctl_temp_status }, { BT_MESH_MODEL_OP_2(0x82, 0x68), 6, light_ctl_default_status }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Light CTL Temp. Server (0x1306) */ static const struct bt_mesh_model_op light_ctl_temp_srv_op[] = { { BT_MESH_MODEL_OP_2(0x82, 0x61), 0, light_ctl_temp_get }, { BT_MESH_MODEL_OP_2(0x82, 0x64), 5, light_ctl_temp_set }, { BT_MESH_MODEL_OP_2(0x82, 0x65), 5, light_ctl_temp_set_unack }, BT_MESH_MODEL_OP_END, }; /* Mapping of message handlers for Vendor (0x4321) */ static const struct bt_mesh_model_op vnd_ops[] = { { BT_MESH_MODEL_OP_3(0x01, CID_RUNTIME), 0, vnd_get }, { BT_MESH_MODEL_OP_3(0x02, CID_RUNTIME), 3, vnd_set }, { BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME), 3, vnd_set_unack }, { BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME), 6, vnd_status }, BT_MESH_MODEL_OP_END, }; struct bt_mesh_model root_models[] = { BT_MESH_MODEL_CFG_SRV, BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, &gen_onoff_srv_pub_root, &gen_onoff_srv_root_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, &gen_onoff_cli_pub_root, NULL), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_srv_op, &gen_level_srv_pub_root, &gen_level_srv_root_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, gen_level_cli_op, &gen_level_cli_pub_root, NULL), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV, gen_def_trans_time_srv_op, &gen_def_trans_time_srv_pub, &gen_def_trans_time_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, gen_def_trans_time_cli_op, &gen_def_trans_time_cli_pub, NULL), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV, gen_power_onoff_srv_op, &gen_power_onoff_srv_pub, &gen_power_onoff_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV, gen_power_onoff_setup_srv_op, &gen_power_onoff_srv_pub, &gen_power_onoff_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, gen_power_onoff_cli_op, &gen_power_onoff_cli_pub, NULL), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, light_lightness_srv_op, &light_lightness_srv_pub, &light_lightness_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV, light_lightness_setup_srv_op, &light_lightness_srv_pub, &light_lightness_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI, light_lightness_cli_op, &light_lightness_cli_pub, NULL), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SRV, light_ctl_srv_op, &light_ctl_srv_pub, &light_ctl_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV, light_ctl_setup_srv_op, &light_ctl_srv_pub, &light_ctl_srv_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_CLI, light_ctl_cli_op, &light_ctl_cli_pub, NULL), }; struct bt_mesh_model vnd_models[] = { BT_MESH_MODEL_VND(CID_RUNTIME, 0x4321, vnd_ops, &vnd_pub, &vnd_user_data), }; struct bt_mesh_model s0_models[] = { BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_srv_op, &gen_level_srv_pub_s0, &gen_level_srv_s0_user_data), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, gen_level_cli_op, &gen_level_cli_pub_s0, NULL), BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV, light_ctl_temp_srv_op, &light_ctl_srv_pub, &light_ctl_srv_user_data), }; static struct bt_mesh_elem elements[] = { BT_MESH_ELEM(0, root_models, vnd_models), BT_MESH_ELEM(0, s0_models, BT_MESH_MODEL_NONE), }; const struct bt_mesh_comp comp = { .cid = CID_RUNTIME, .elem = elements, .elem_count = ARRAY_SIZE(elements), };