ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
zigbee_zephyr.cpp
Go to the documentation of this file.
1#include "zigbee_zephyr.h"
2#if defined(USE_ZIGBEE) && defined(USE_NRF52)
3#include "esphome/core/log.h"
4#include <zephyr/settings/settings.h>
5#include <zephyr/storage/flash_map.h>
6#include "esphome/core/hal.h"
7
8extern "C" {
9#include <zboss_api.h>
10#include <zboss_api_addons.h>
11#include <zb_nrf_platform.h>
12#include <zigbee/zigbee_app_utils.h>
13#include <zb_error_to_string.h>
14}
15
16namespace esphome::zigbee {
17
18static const char *const TAG = "zigbee";
19
20ZigbeeComponent *global_zigbee = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
21
22const uint8_t IEEE_ADDR_BUF_SIZE = 17;
23
25 zb_zdo_app_signal_hdr_t *sig_hndler = nullptr;
26 zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, &sig_hndler);
27 zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid);
28
29 switch (sig) {
30 case ZB_ZDO_SIGNAL_SKIP_STARTUP:
31 ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_SKIP_STARTUP, status: %d", status);
32 break;
33 case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
34 ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY, status: %d", status);
35 break;
36 case ZB_ZDO_SIGNAL_LEAVE:
37 ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_LEAVE, status: %d", status);
38 break;
39 case ZB_BDB_SIGNAL_DEVICE_REBOOT:
40 ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_REBOOT, status: %d", status);
41 if (status == RET_OK) {
42 on_join_();
43 }
44 break;
45 case ZB_BDB_SIGNAL_STEERING:
46 break;
47 case ZB_COMMON_SIGNAL_CAN_SLEEP:
48 ESP_LOGV(TAG, "ZB_COMMON_SIGNAL_CAN_SLEEP, status: %d", status);
49 break;
50 case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
51 ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_FIRST_START, status: %d", status);
52 break;
53 case ZB_NLME_STATUS_INDICATION:
54 ESP_LOGD(TAG, "ZB_NLME_STATUS_INDICATION, status: %d", status);
55 break;
56 case ZB_BDB_SIGNAL_TC_REJOIN_DONE:
57 ESP_LOGD(TAG, "ZB_BDB_SIGNAL_TC_REJOIN_DONE, status: %d", status);
58 break;
59 default:
60 ESP_LOGD(TAG, "zboss_signal_handler sig: %d, status: %d", sig, status);
61 break;
62 }
63
64 auto before = millis();
65 auto err = zigbee_default_signal_handler(bufid);
66 if (err != RET_OK) {
67 ESP_LOGE(TAG, "Zigbee_default_signal_handler ERROR %u [%s]", err, zb_error_to_string_get(err));
68 }
69
70 if (sig == ZB_COMMON_SIGNAL_CAN_SLEEP) {
71 this->sleep_remainder_ += millis() - before;
72 uint32_t seconds = this->sleep_remainder_ / 1000;
73 this->sleep_remainder_ -= seconds * 1000;
74 this->sleep_time_ += seconds;
75 }
76
77 switch (sig) {
78 case ZB_BDB_SIGNAL_STEERING:
79 ESP_LOGD(TAG, "ZB_BDB_SIGNAL_STEERING, status: %d", status);
80 if (status == RET_OK) {
81 zb_ext_pan_id_t extended_pan_id;
82 char ieee_addr_buf[IEEE_ADDR_BUF_SIZE] = {0};
83 int addr_len;
84
85 zb_get_extended_pan_id(extended_pan_id);
86 addr_len = ieee_addr_to_str(ieee_addr_buf, sizeof(ieee_addr_buf), extended_pan_id);
87
88 for (int i = 0; i < addr_len; ++i) {
89 if (ieee_addr_buf[i] != '0') {
90 on_join_();
91 break;
92 }
93 }
94 }
95 break;
96 }
97
98 /* All callbacks should either reuse or free passed buffers.
99 * If bufid == 0, the buffer is invalid (not passed).
100 */
101 if (bufid) {
102 zb_buf_free(bufid);
103 }
104}
105
106void ZigbeeComponent::zcl_device_cb(zb_bufid_t bufid) {
107 zb_zcl_device_callback_param_t *p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
108 zb_zcl_device_callback_id_t device_cb_id = p_device_cb_param->device_cb_id;
109 zb_uint16_t cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
110 zb_uint16_t attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
111 auto endpoint = p_device_cb_param->endpoint;
112
113 ESP_LOGI(TAG, "%s id %hd, cluster_id %d, attr_id %d, endpoint: %d", __func__, device_cb_id, cluster_id, attr_id,
114 endpoint);
115
116 /* Set default response value. */
117 p_device_cb_param->status = RET_OK;
118
119 // endpoints are enumerated from 1
120 if (global_zigbee->callbacks_.size() >= endpoint) {
121 const auto &cb = global_zigbee->callbacks_[endpoint - 1];
122 if (cb) {
123 cb(bufid);
124 return;
125 }
126 }
127 p_device_cb_param->status = RET_NOT_IMPLEMENTED;
128}
129
131 this->defer([this]() {
132 ESP_LOGD(TAG, "Joined the network");
133 this->join_trigger_.trigger();
134 this->join_cb_.call();
135 });
136}
137
138#ifdef USE_ZIGBEE_WIPE_ON_BOOT
140 const struct flash_area *fap;
141 flash_area_open(area, &fap);
142 flash_area_erase(fap, 0, fap->fa_size);
143 flash_area_close(fap);
144}
145#endif
146
148 global_zigbee = this;
149 auto err = settings_subsys_init();
150 if (err) {
151 ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err);
152 return;
153 }
154
155#ifdef USE_ZIGBEE_WIPE_ON_BOOT
156 bool wipe = true;
157#ifdef USE_ZIGBEE_WIPE_ON_BOOT_MAGIC
158 // unique hash to store preferences for this component
159 uint32_t hash = 88498616UL;
160 uint32_t wipe_value = 0;
161 auto wipe_pref = global_preferences->make_preference<uint32_t>(hash, true);
162 if (wipe_pref.load(&wipe_value)) {
163 wipe = wipe_value != USE_ZIGBEE_WIPE_ON_BOOT_MAGIC;
164 ESP_LOGD(TAG, "Wipe value in preferences %u, in firmware %u", wipe_value, USE_ZIGBEE_WIPE_ON_BOOT_MAGIC);
165 }
166#endif
167 if (wipe) {
168 erase_flash_(FIXED_PARTITION_ID(ZBOSS_NVRAM));
169 erase_flash_(FIXED_PARTITION_ID(ZBOSS_PRODUCT_CONFIG));
170 erase_flash_(FIXED_PARTITION_ID(SETTINGS_STORAGE));
171#ifdef USE_ZIGBEE_WIPE_ON_BOOT_MAGIC
172 wipe_value = USE_ZIGBEE_WIPE_ON_BOOT_MAGIC;
173 wipe_pref.save(&wipe_value);
174#endif
175 }
176#endif
177
178 ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
179 err = settings_load();
180 if (err) {
181 ESP_LOGE(TAG, "Cannot load settings, err: %d", err);
182 return;
183 }
184 zigbee_enable();
185}
186
187static const char *role() {
188 switch (zb_get_network_role()) {
189 case ZB_NWK_DEVICE_TYPE_COORDINATOR:
190 return "coordinator";
191 case ZB_NWK_DEVICE_TYPE_ROUTER:
192 return "router";
193 case ZB_NWK_DEVICE_TYPE_ED:
194 return "end device";
195 }
196 return "unknown";
197}
198
199static const char *get_wipe_on_boot() {
200#ifdef USE_ZIGBEE_WIPE_ON_BOOT
201#ifdef USE_ZIGBEE_WIPE_ON_BOOT_MAGIC
202 return "ONCE";
203#else
204 return "YES";
205#endif
206#else
207 return "NO";
208#endif
209}
210
212 char ieee_addr_buf[IEEE_ADDR_BUF_SIZE] = {0};
213 zb_ieee_addr_t addr;
214 zb_get_long_address(addr);
215 ieee_addr_to_str(ieee_addr_buf, sizeof(ieee_addr_buf), addr);
216 zb_ext_pan_id_t extended_pan_id;
217 char extended_pan_id_buf[IEEE_ADDR_BUF_SIZE] = {0};
218 zb_get_extended_pan_id(extended_pan_id);
219 ieee_addr_to_str(extended_pan_id_buf, sizeof(extended_pan_id_buf), extended_pan_id);
220 ESP_LOGCONFIG(TAG,
221 "Zigbee\n"
222 " Wipe on boot: %s\n"
223 " Device is joined to the network: %s\n"
224 " Sleep time: %us\n"
225 " Current channel: %d\n"
226 " Current page: %d\n"
227 " Sleep threshold: %ums\n"
228 " Role: %s\n"
229 " Long addr: 0x%s\n"
230 " Short addr: 0x%04X\n"
231 " Long pan id: 0x%s\n"
232 " Short pan id: 0x%04X",
233 get_wipe_on_boot(), YESNO(zb_zdo_joined()), this->sleep_time_, zb_get_current_channel(),
234 zb_get_current_page(), zb_get_sleep_threshold(), role(), ieee_addr_buf, zb_get_short_address(),
235 extended_pan_id_buf, zb_get_pan_id());
237}
238
239static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) {
240 ESP_LOGD(TAG, "Force zboss scheduler to wake and send attribute report");
241 zb_buf_free(bufid);
242}
243
245
247 if (this->force_report_) {
248 this->force_report_ = false;
249 zb_buf_get_out_delayed_ext(send_attribute_report, 0, 0);
250 }
251}
252
254 ESP_LOGD(TAG, "Factory reset");
255 ZB_SCHEDULE_APP_CALLBACK(zb_bdb_reset_via_local_action, 0);
256}
257
259#ifdef ESPHOME_LOG_HAS_VERBOSE
260 auto now = millis();
261 bool first = true;
262 for (zb_uint8_t j = 0; j < ZCL_CTX().device_ctx->ep_count; j++) {
263 if (ZCL_CTX().device_ctx->ep_desc_list[j]->reporting_info) {
264 zb_zcl_reporting_info_t *rep_info = ZCL_CTX().device_ctx->ep_desc_list[j]->reporting_info;
265 for (zb_uint8_t i = 0; i < ZCL_CTX().device_ctx->ep_desc_list[j]->rep_info_count; i++) {
266 if (!first) {
267 ESP_LOGV(TAG, "");
268 }
269 first = false;
270 ESP_LOGV(TAG, "Endpoint: %d, cluster_id %d, attr_id %d, flags %d, report in %ums", rep_info->ep,
271 rep_info->cluster_id, rep_info->attr_id, rep_info->flags,
272 ZB_ZCL_GET_REPORTING_FLAG(rep_info, ZB_ZCL_REPORT_TIMER_STARTED)
273 ? ZB_TIME_BEACON_INTERVAL_TO_MSEC(rep_info->run_time) - now
274 : 0);
275 ESP_LOGV(TAG, "Min_interval %ds, max_interval %ds, def_min_interval %ds, def_max_interval %ds",
276 rep_info->u.send_info.min_interval, rep_info->u.send_info.max_interval,
277 rep_info->u.send_info.def_min_interval, rep_info->u.send_info.def_max_interval);
278 rep_info++;
279 }
280 }
281 }
282#endif
283}
284
285} // namespace esphome::zigbee
286
287extern "C" void zboss_signal_handler(zb_uint8_t param) {
289}
290#endif
uint8_t status
Definition bl0942.h:8
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition component.h:479
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:279
static void zcl_device_cb(zb_bufid_t bufid)
CallbackManager< void()> join_cb_
std::array< std::function< void(zb_bufid_t bufid)>, ZIGBEE_ENDPOINTS_COUNT > callbacks_
void zboss_signal_handler_esphome(zb_bufid_t bufid)
uint16_t addr_len
const char *const TAG
Definition spi.cpp:7
ZigbeeComponent * global_zigbee
const uint8_t IEEE_ADDR_BUF_SIZE
ESPPreferences * global_preferences
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:25
void zboss_signal_handler(zb_uint8_t param)