/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models * * SPDX-FileCopyrightText: 2018 Vikrant More * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "btc_ble_mesh_generic_model.h" #include "btc_ble_mesh_lighting_model.h" #include "btc_ble_mesh_time_scene_model.h" #include "btc_ble_mesh_sensor_model.h" #include "mesh/config.h" #include "mesh/model_opcode.h" #include "mesh/state_transition.h" #if (CONFIG_BLE_MESH_GENERIC_SERVER || \ CONFIG_BLE_MESH_TIME_SCENE_SERVER || \ CONFIG_BLE_MESH_LIGHTING_SERVER) /* Function to calculate Remaining Time (Start) */ void bt_mesh_server_calc_remain_time(struct bt_mesh_state_transition *transition) { uint8_t steps = 0U, resolution = 0U; int32_t duration_remainder = 0; int64_t now = 0; if (transition->just_started) { transition->remain_time = transition->trans_time; } else { now = k_uptime_get(); duration_remainder = transition->total_duration - (now - transition->start_timestamp); if (duration_remainder > 620000) { /* > 620 seconds -> resolution = 0b11 [10 minutes] */ resolution = 0x03; steps = duration_remainder / 600000; } else if (duration_remainder > 62000) { /* > 62 seconds -> resolution = 0b10 [10 seconds] */ resolution = 0x02; steps = duration_remainder / 10000; } else if (duration_remainder > 6200) { /* > 6.2 seconds -> resolution = 0b01 [1 seconds] */ resolution = 0x01; steps = duration_remainder / 1000; } else if (duration_remainder > 0) { /* <= 6.2 seconds -> resolution = 0b00 [100 ms] */ resolution = 0x00; steps = duration_remainder / 100; } else { resolution = 0x00; steps = 0x00; } transition->remain_time = (resolution << 6) | steps; } } /* Function to calculate Remaining Time (End) */ static void tt_values_calculator(struct bt_mesh_state_transition *transition) { uint8_t steps_multiplier = 0U, resolution = 0U; resolution = (transition->trans_time >> 6); steps_multiplier = (transition->trans_time & 0x3F); switch (resolution) { case 0: /* 100ms */ transition->total_duration = steps_multiplier * 100; break; case 1: /* 1 second */ transition->total_duration = steps_multiplier * 1000; break; case 2: /* 10 seconds */ transition->total_duration = steps_multiplier * 10000; break; case 3: /* 10 minutes */ transition->total_duration = steps_multiplier * 600000; break; } transition->counter = ((float) transition->total_duration / 100); if (transition->counter > BLE_MESH_DEVICE_SPECIFIC_RESOLUTION) { transition->counter = BLE_MESH_DEVICE_SPECIFIC_RESOLUTION; } } static void transition_time_values(struct bt_mesh_state_transition *transition, uint8_t trans_time, uint8_t delay) { transition->trans_time = trans_time; transition->delay = delay; if (trans_time == 0U) { return; } tt_values_calculator(transition); transition->quo_tt = transition->total_duration / transition->counter; } static void transition_timer_start(struct bt_mesh_state_transition *transition) { transition->start_timestamp = k_uptime_get(); k_delayed_work_submit_periodic(&transition->timer, K_MSEC(transition->quo_tt)); bt_mesh_atomic_set_bit(transition->flag, BLE_MESH_TRANS_TIMER_START); } static void transition_timer_stop(struct bt_mesh_state_transition *transition) { k_delayed_work_cancel(&transition->timer); bt_mesh_atomic_clear_bit(transition->flag, BLE_MESH_TRANS_TIMER_START); } #if CONFIG_BLE_MESH_GENERIC_SERVER void generic_onoff_tt_values(struct bt_mesh_gen_onoff_srv *srv, uint8_t trans_time, uint8_t delay) { return transition_time_values(&srv->transition, trans_time, delay); } void generic_level_tt_values(struct bt_mesh_gen_level_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_level = ((float) (srv->state.level - srv->state.target_level) / srv->transition.counter); } void generic_power_level_tt_values(struct bt_mesh_gen_power_level_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_level = ((float) (srv->state->power_actual - srv->state->target_power_actual) / srv->transition.counter); } #endif /* CONFIG_BLE_MESH_GENERIC_SERVER */ #if CONFIG_BLE_MESH_LIGHTING_SERVER void light_lightness_actual_tt_values(struct bt_mesh_light_lightness_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->actual_transition, trans_time, delay); srv->tt_delta_lightness_actual = ((float) (srv->state->lightness_actual - srv->state->target_lightness_actual) / srv->actual_transition.counter); } void light_lightness_linear_tt_values(struct bt_mesh_light_lightness_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->linear_transition, trans_time, delay); srv->tt_delta_lightness_linear = ((float) (srv->state->lightness_linear - srv->state->target_lightness_linear) / srv->linear_transition.counter); } void light_ctl_tt_values(struct bt_mesh_light_ctl_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_lightness = ((float) (srv->state->lightness - srv->state->target_lightness) / srv->transition.counter); srv->tt_delta_temperature = ((float) (srv->state->temperature - srv->state->target_temperature) / srv->transition.counter); srv->tt_delta_delta_uv = ((float) (srv->state->delta_uv - srv->state->target_delta_uv) / srv->transition.counter); } void light_ctl_temp_tt_values(struct bt_mesh_light_ctl_temp_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_temperature = ((float) (srv->state->temperature - srv->state->target_temperature) / srv->transition.counter); srv->tt_delta_delta_uv = ((float) (srv->state->delta_uv - srv->state->target_delta_uv) / srv->transition.counter); } void light_hsl_tt_values(struct bt_mesh_light_hsl_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_lightness = ((float) (srv->state->lightness - srv->state->target_lightness) / srv->transition.counter); srv->tt_delta_hue = ((float) (srv->state->hue - srv->state->target_hue) / srv->transition.counter); srv->tt_delta_saturation = ((float) (srv->state->saturation - srv->state->target_saturation) / srv->transition.counter); } void light_hsl_hue_tt_values(struct bt_mesh_light_hsl_hue_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_hue = ((float) (srv->state->hue - srv->state->target_hue) / srv->transition.counter); } void light_hsl_sat_tt_values(struct bt_mesh_light_hsl_sat_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_saturation = ((float) (srv->state->saturation - srv->state->target_saturation) / srv->transition.counter); } void light_xyl_tt_values(struct bt_mesh_light_xyl_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); srv->tt_delta_lightness = ((float) (srv->state->lightness - srv->state->target_lightness) / srv->transition.counter); srv->tt_delta_x = ((float) (srv->state->x - srv->state->target_x) / srv->transition.counter); srv->tt_delta_y = ((float) (srv->state->y - srv->state->target_y) / srv->transition.counter); } void light_lc_tt_values(struct bt_mesh_light_lc_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); } #endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */ #if CONFIG_BLE_MESH_TIME_SCENE_SERVER void scene_tt_values(struct bt_mesh_scene_srv *srv, uint8_t trans_time, uint8_t delay) { transition_time_values(&srv->transition, trans_time, delay); } #endif /* CONFIG_BLE_MESH_TIME_SCENE_SERVER */ /* Timers related handlers & threads (Start) */ #if CONFIG_BLE_MESH_GENERIC_SERVER void generic_onoff_work_handler(struct k_work *work) { struct bt_mesh_gen_onoff_srv *srv = CONTAINER_OF(work, struct bt_mesh_gen_onoff_srv, transition.timer.work); bt_mesh_gen_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_generic_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.gen_onoff_set.onoff = srv->state.onoff; bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { /** * Because binary states cannot support transitions, when changing to * 0x01 (On), the Generic OnOff state shall change immediately when * the transition starts, and when changing to 0x00, the state shall * change when the transition finishes. */ if (srv->state.target_onoff == BLE_MESH_STATE_ON) { srv->state.onoff = BLE_MESH_STATE_ON; change.gen_onoff_set.onoff = srv->state.onoff; bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); } transition_timer_start(&srv->transition); } bt_mesh_generic_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state.onoff = srv->state.target_onoff; if (srv->state.target_onoff != BLE_MESH_STATE_ON) { change.gen_onoff_set.onoff = srv->state.onoff; bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); } } gen_onoff_publish(srv->model); bt_mesh_generic_server_unlock(); } void generic_level_work_handler(struct k_work *work) { struct bt_mesh_gen_level_srv *srv = CONTAINER_OF(work, struct bt_mesh_gen_level_srv, transition.timer.work); bt_mesh_gen_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_generic_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { switch (ctx->recv_op) { case BLE_MESH_MODEL_OP_GEN_LEVEL_SET: case BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK: change.gen_level_set.level = srv->state.level; break; case BLE_MESH_MODEL_OP_GEN_DELTA_SET: case BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK: change.gen_delta_set.level = srv->state.level; break; case BLE_MESH_MODEL_OP_GEN_MOVE_SET: case BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK: change.gen_move_set.level = srv->state.level; break; } bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_generic_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state.level -= srv->tt_delta_level; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state.level = srv->state.target_level; } switch (ctx->recv_op) { case BLE_MESH_MODEL_OP_GEN_LEVEL_SET: case BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK: change.gen_level_set.level = srv->state.level; break; case BLE_MESH_MODEL_OP_GEN_DELTA_SET: case BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK: change.gen_delta_set.level = srv->state.level; break; case BLE_MESH_MODEL_OP_GEN_MOVE_SET: case BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK: change.gen_move_set.level = srv->state.level; break; } bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); gen_level_publish(srv->model); bt_mesh_generic_server_unlock(); } void generic_power_level_work_handler(struct k_work *work) { struct bt_mesh_gen_power_level_srv *srv = CONTAINER_OF(work, struct bt_mesh_gen_power_level_srv, transition.timer.work); bt_mesh_gen_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_generic_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.gen_power_level_set.power = srv->state->power_actual; bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_generic_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->power_actual -= srv->tt_delta_level; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->power_actual = srv->state->target_power_actual; /** * Whenever the Generic Power Actual state is changed to a non-zero value * as a result of a non-transactional message or a completed sequence of * transactional messages, the value of the Generic Power Last state shall * be set to the value of the Generic Power Actual state. */ if (srv->state->power_actual) { srv->state->power_last = srv->state->power_actual; } } change.gen_power_level_set.power = srv->state->power_actual; bt_mesh_generic_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_GENERIC_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); gen_power_level_publish(srv->model, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS); bt_mesh_generic_server_unlock(); } #endif /* CONFIG_BLE_MESH_GENERIC_SERVER */ #if CONFIG_BLE_MESH_LIGHTING_SERVER void light_lightness_actual_work_handler(struct k_work *work) { struct bt_mesh_light_lightness_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_lightness_srv, actual_transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->actual_transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->actual_transition.timer.work.user_data; if (srv->actual_transition.just_started) { srv->actual_transition.just_started = false; if (srv->actual_transition.counter == 0U) { change.lightness_set.lightness = srv->state->lightness_actual; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->actual_transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->actual_transition); } bt_mesh_light_server_unlock(); return; } if (srv->actual_transition.counter != 0U) { srv->actual_transition.counter--; srv->state->lightness_actual -= srv->tt_delta_lightness_actual; } if (srv->actual_transition.counter == 0U) { transition_timer_stop(&srv->actual_transition); srv->state->lightness_actual = srv->state->target_lightness_actual; /** * Whenever the Light Lightness Actual state is changed with a non- * transactional message or a completed sequence of transactional * messages to a non-zero value, the value of the Light Lightness * Last shall be set to the value of the Light Lightness Actual. */ if (srv->state->lightness_actual) { srv->state->lightness_last = srv->state->lightness_actual; } } change.lightness_set.lightness = srv->state->lightness_actual; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_lightness_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS); bt_mesh_light_server_unlock(); } void light_lightness_linear_work_handler(struct k_work *work) { struct bt_mesh_light_lightness_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_lightness_srv, linear_transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->linear_transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->linear_transition.timer.work.user_data; if (srv->linear_transition.just_started) { srv->linear_transition.just_started = false; if (srv->linear_transition.counter == 0U) { change.lightness_linear_set.lightness = srv->state->lightness_linear; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->linear_transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->linear_transition); } bt_mesh_light_server_unlock(); return; } if (srv->linear_transition.counter != 0U) { srv->linear_transition.counter--; srv->state->lightness_linear -= srv->tt_delta_lightness_linear; } if (srv->linear_transition.counter == 0U) { transition_timer_stop(&srv->linear_transition); srv->state->lightness_linear = srv->state->target_lightness_linear; } change.lightness_linear_set.lightness = srv->state->lightness_linear; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_lightness_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS); bt_mesh_light_server_unlock(); } void light_ctl_work_handler(struct k_work *work) { struct bt_mesh_light_ctl_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_ctl_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.ctl_set.lightness = srv->state->lightness; change.ctl_set.temperature = srv->state->temperature; change.ctl_set.delta_uv = srv->state->delta_uv; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->lightness -= srv->tt_delta_lightness; srv->state->temperature -= srv->tt_delta_temperature; srv->state->delta_uv -= srv->tt_delta_delta_uv; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->lightness = srv->state->target_lightness; srv->state->temperature = srv->state->target_temperature; srv->state->delta_uv = srv->state->target_delta_uv; } change.ctl_set.lightness = srv->state->lightness; change.ctl_set.temperature = srv->state->temperature; change.ctl_set.delta_uv = srv->state->delta_uv; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_ctl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS); bt_mesh_light_server_unlock(); } void light_ctl_temp_work_handler(struct k_work *work) { struct bt_mesh_light_ctl_temp_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_ctl_temp_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.ctl_temp_set.temperature = srv->state->temperature; change.ctl_temp_set.delta_uv = srv->state->delta_uv; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->temperature -= srv->tt_delta_temperature; srv->state->delta_uv -= srv->tt_delta_delta_uv; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->temperature = srv->state->target_temperature; srv->state->delta_uv = srv->state->target_delta_uv; } change.ctl_temp_set.temperature = srv->state->temperature; change.ctl_temp_set.delta_uv = srv->state->delta_uv; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_ctl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS); bt_mesh_light_server_unlock(); } void light_hsl_work_handler(struct k_work *work) { struct bt_mesh_light_hsl_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_hsl_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.hsl_set.lightness = srv->state->lightness; change.hsl_set.hue = srv->state->hue; change.hsl_set.saturation = srv->state->saturation; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->lightness -= srv->tt_delta_lightness; srv->state->hue -= srv->tt_delta_hue; srv->state->saturation -= srv->tt_delta_saturation; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->lightness = srv->state->target_lightness; srv->state->hue = srv->state->target_hue; srv->state->saturation = srv->state->target_saturation; } change.hsl_set.lightness = srv->state->lightness; change.hsl_set.hue = srv->state->hue; change.hsl_set.saturation = srv->state->saturation; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_hsl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS); bt_mesh_light_server_unlock(); } void light_hsl_hue_work_handler(struct k_work *work) { struct bt_mesh_light_hsl_hue_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_hsl_hue_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.hsl_hue_set.hue = srv->state->hue; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->hue -= srv->tt_delta_hue; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->hue = srv->state->target_hue; } change.hsl_hue_set.hue = srv->state->hue; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_hsl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS); bt_mesh_light_server_unlock(); } void light_hsl_sat_work_handler(struct k_work *work) { struct bt_mesh_light_hsl_sat_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_hsl_sat_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.hsl_saturation_set.saturation = srv->state->saturation; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->saturation -= srv->tt_delta_saturation; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->saturation = srv->state->target_saturation; } change.hsl_saturation_set.saturation = srv->state->saturation; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_hsl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS); bt_mesh_light_server_unlock(); } void light_xyl_work_handler(struct k_work *work) { struct bt_mesh_light_xyl_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_xyl_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.xyl_set.lightness = srv->state->lightness; change.xyl_set.x = srv->state->x; change.xyl_set.y = srv->state->y; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; srv->state->lightness -= srv->tt_delta_lightness; srv->state->x -= srv->tt_delta_x; srv->state->y -= srv->tt_delta_y; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->lightness = srv->state->target_lightness; srv->state->x = srv->state->target_x; srv->state->y = srv->state->target_y; } change.xyl_set.lightness = srv->state->lightness; change.xyl_set.x = srv->state->x; change.xyl_set.y = srv->state->y; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); light_xyl_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS); bt_mesh_light_server_unlock(); } void light_lc_work_handler(struct k_work *work) { struct bt_mesh_light_lc_srv *srv = CONTAINER_OF(work, struct bt_mesh_light_lc_srv, transition.timer.work); bt_mesh_light_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_light_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.lc_light_onoff_set.onoff = srv->lc->state.light_onoff; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { /** * Because binary states cannot support transitions, when changing to * 0x01 (On), the Generic OnOff state shall change immediately when * the transition starts, and when changing to 0x00, the state shall * change when the transition finishes. */ if (srv->lc->state.target_light_onoff == BLE_MESH_STATE_ON) { srv->lc->state.light_onoff = BLE_MESH_STATE_ON; bt_mesh_light_server_state_change_t change = { .lc_light_onoff_set.onoff = srv->lc->state.light_onoff, }; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); } transition_timer_start(&srv->transition); } bt_mesh_light_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->lc->state.light_onoff = srv->lc->state.target_light_onoff; if (srv->lc->state.light_onoff != BLE_MESH_STATE_ON) { change.lc_light_onoff_set.onoff = srv->lc->state.light_onoff; bt_mesh_lighting_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_LIGHTING_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); } } light_lc_publish(srv->model, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS); bt_mesh_light_server_unlock(); } #endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */ #if CONFIG_BLE_MESH_TIME_SCENE_SERVER void scene_recall_work_handler(struct k_work *work) { struct bt_mesh_scene_srv *srv = CONTAINER_OF(work, struct bt_mesh_scene_srv, transition.timer.work); bt_mesh_time_scene_server_state_change_t change = {0}; struct bt_mesh_msg_ctx *ctx = NULL; if (srv == NULL || srv->state == NULL || srv->transition.timer.work.user_data == NULL) { BT_ERR("%s, Invalid parameter", __func__); return; } bt_mesh_time_scene_server_lock(); ctx = (struct bt_mesh_msg_ctx *)srv->transition.timer.work.user_data; if (srv->transition.just_started) { srv->transition.just_started = false; if (srv->transition.counter == 0U) { change.scene_recall.scene_number = srv->state->current_scene; bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); bt_mesh_atomic_clear_bit(srv->transition.flag, BLE_MESH_TRANS_TIMER_START); } else { transition_timer_start(&srv->transition); } bt_mesh_time_scene_server_unlock(); return; } if (srv->transition.counter != 0U) { srv->transition.counter--; } if (srv->transition.counter == 0U) { transition_timer_stop(&srv->transition); srv->state->current_scene = srv->state->target_scene; srv->state->in_progress = false; srv->state->target_scene = INVALID_SCENE_NUMBER; } change.scene_recall.scene_number = srv->state->current_scene; bt_mesh_time_scene_server_cb_evt_to_btc(BTC_BLE_MESH_EVT_TIME_SCENE_SERVER_STATE_CHANGE, srv->model, ctx, (const uint8_t *)&change, sizeof(change)); scene_publish(srv->model, ctx, BLE_MESH_MODEL_OP_SCENE_STATUS); bt_mesh_time_scene_server_unlock(); } #endif /* CONFIG_BLE_MESH_TIME_SCENE_SERVER */ /* Timers related handlers & threads (End) */ void bt_mesh_server_stop_transition(struct bt_mesh_state_transition *transition) { memset(transition, 0x0, offsetof(struct bt_mesh_state_transition, flag)); if (bt_mesh_atomic_test_and_clear_bit(transition->flag, BLE_MESH_TRANS_TIMER_START)) { k_delayed_work_cancel(&transition->timer); } } void bt_mesh_server_start_transition(struct bt_mesh_state_transition *transition) { k_delayed_work_submit(&transition->timer, K_MSEC(5 * transition->delay)); if (transition->delay) { bt_mesh_atomic_set_bit(transition->flag, BLE_MESH_TRANS_TIMER_START); } } /* Messages handlers (End) */ #endif /* (CONFIG_BLE_MESH_GENERIC_SERVER || \ CONFIG_BLE_MESH_TIME_SCENE_SERVER || \ CONFIG_BLE_MESH_LIGHTING_SERVER) */