From 3353e71f9a92651d1786e5aa4fa1da3fb1bee26d Mon Sep 17 00:00:00 2001 From: luar123 <49960470+luar123@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:34:32 +0200 Subject: [PATCH] [zigbee] Improve network handling on esp32 (2/3) (#18008) --- esphome/components/zigbee/zigbee_esp32.cpp | 52 ++++++++++++++++++---- esphome/components/zigbee/zigbee_esp32.h | 18 +++----- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/esphome/components/zigbee/zigbee_esp32.cpp b/esphome/components/zigbee/zigbee_esp32.cpp index dcfdf2f3e1..3500aa3382 100644 --- a/esphome/components/zigbee/zigbee_esp32.cpp +++ b/esphome/components/zigbee/zigbee_esp32.cpp @@ -36,6 +36,17 @@ uint8_t *get_zcl_string(const char *str, uint8_t max_size, bool use_max_size) { return zcl_str; } +void ZigbeeComponent::factory_reset() { + esp_zigbee_lock_acquire(portMAX_DELAY); + if (this->joined_) { + // send leave request and trigger EZB_ZDO_SIGNAL_LEAVE + ezb_bdb_reset_via_local_action(); + } else { + esp_zigbee_factory_reset(); // triggers a reboot + } + esp_zigbee_lock_release(); +} + void ZigbeeComponent::esp_zigbee_alarm_bdb_commissioning(ezb_bdb_comm_mode_mask_t mode) { if (!esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) { global_zigbee->set_timeout("zb_init", 10, [mode]() { ZigbeeComponent::esp_zigbee_alarm_bdb_commissioning(mode); }); @@ -53,7 +64,7 @@ bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) { switch (signal_type) { case EZB_ZDO_SIGNAL_SKIP_STARTUP: ESP_LOGD(TAG, "Zigbee stack initialized"); - global_zigbee->started = true; + global_zigbee->started_ = true; ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION); break; case EZB_BDB_SIGNAL_DEVICE_FIRST_START: @@ -62,12 +73,13 @@ bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) { if (status == EZB_BDB_STATUS_SUCCESS) { ESP_LOGD(TAG, "Device started up in %sfactory-reset mode", ezb_bdb_is_factory_new() ? "" : "non "); if (ezb_bdb_is_factory_new()) { - global_zigbee->factory_new = true; + global_zigbee->factory_new_ = true; ESP_LOGD(TAG, "Start network steering"); ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_STEERING); } else { ESP_LOGD(TAG, "Device rebooted"); - global_zigbee->joined = true; + global_zigbee->joined_ = true; + global_zigbee->join_pending_ = true; global_zigbee->enable_loop_soon_any_context(); } } else { @@ -85,7 +97,8 @@ bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) { ezb_nwk_get_extended_panid(&extended_pan_id); ESP_LOGD(TAG, "Joined network successfully: PAN ID(0x%04hx, EXT: 0x%llx), Channel(%d), Short Address(0x%04hx)", ezb_nwk_get_panid(), extended_pan_id.u64, ezb_nwk_get_current_channel(), ezb_nwk_get_short_address()); - global_zigbee->joined = true; + global_zigbee->joined_ = true; + global_zigbee->join_pending_ = true; global_zigbee->enable_loop_soon_any_context(); } else { ESP_LOGD(TAG, "Failed to join network with status(0x%02x)", status); @@ -105,7 +118,29 @@ bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) { const ezb_zdo_signal_leave_params_t *leave_params = (const ezb_zdo_signal_leave_params_t *) ezb_app_signal_get_params(app_signal); if (leave_params->leave_type == EZB_ZDO_LEAVE_TYPE_RESET) { - esp_zigbee_factory_reset(); + esp_zigbee_factory_reset(); // triggers a reboot + } + global_zigbee->joined_ = false; + } break; + case EZB_NWK_SIGNAL_NETWORK_STATUS: { + const ezb_nwk_signal_network_status_params_t *network_status_params = + (const ezb_nwk_signal_network_status_params_t *) ezb_app_signal_get_params(app_signal); + if (network_status_params->status == EZB_NWK_NETWORK_STATUS_PARENT_LINK_FAILURE) { + global_zigbee->joined_ = false; + ESP_LOGW(TAG, "Parent link failure, attempting rejoin"); + ezb_zdo_nwk_mgmt_leave_req_t leave_req = { + .dst_nwk_addr = ezb_nwk_get_short_address(), + .field = + { + .remove_children = false, + .rejoin = true, + }, + }; + // Send leave request to the network to rejoin + // triggers EZB_ZDO_SIGNAL_LEAVE signal first, then EZB_BDB_SIGNAL_DEVICE_REBOOT + ezb_zdo_nwk_mgmt_leave_req(&leave_req); + } else { + ESP_LOGD(TAG, "Zigbee APP Signal NETWORK_STATUS: 0x%02x", network_status_params->status); } } break; default: @@ -303,10 +338,9 @@ void ZigbeeComponent::setup() { } void ZigbeeComponent::loop() { - if (!this->join_reported_ && this->joined) { - this->join_reported_ = true; - this->join_cb_.call(this->factory_new); - this->factory_new = false; + if (this->join_pending_.exchange(false)) { + this->join_cb_.call(this->factory_new_); + this->factory_new_ = false; } this->disable_loop(); } diff --git a/esphome/components/zigbee/zigbee_esp32.h b/esphome/components/zigbee/zigbee_esp32.h index 986ffea449..412b7ac4da 100644 --- a/esphome/components/zigbee/zigbee_esp32.h +++ b/esphome/components/zigbee/zigbee_esp32.h @@ -54,11 +54,7 @@ class ZigbeeComponent final : public Component { static bool app_signal_handler(const ezb_app_signal_t *app_signal); static void esp_zigbee_alarm_bdb_commissioning(ezb_bdb_comm_mode_mask_t mode); - void factory_reset() { - esp_zigbee_lock_acquire(portMAX_DELAY); - esp_zigbee_factory_reset(); // triggers a reboot - esp_zigbee_lock_release(); - } + void factory_reset(); template void add_on_join_callback(F &&cb) { this->join_cb_.add(std::forward(cb)); } @@ -66,13 +62,10 @@ class ZigbeeComponent final : public Component { // True after the Zigbee stack has been initialized and the device has started up. Is set before the stack started // network commissioning or has joined a network and won't be reset until the device is rebooted. - bool is_started() { return this->started; } + bool is_started() { return this->started_; } // True if the device has joined a network and is ready to send and receive messages. - bool is_joined() { return this->joined; } - std::atomic started = false; - std::atomic joined = false; - std::atomic factory_new = false; + bool is_joined() { return this->joined_; } protected: struct { @@ -95,8 +88,11 @@ class ZigbeeComponent final : public Component { // key tuple could be replaced by single 64 (48) bit int with bit fields for endpoint, cluster, role and attr_id std::map, ZigbeeAttribute *> attributes_; ezb_af_device_desc_t dev_desc_; - bool join_reported_{false}; CallbackManager join_cb_{}; + std::atomic started_ = false; + std::atomic joined_ = false; + std::atomic join_pending_ = false; + std::atomic factory_new_ = false; }; template