[zigbee] add on_join trigger for esp32 (#16060)
CI / Create common environment (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Check import esphome.__main__ time (push) Has been cancelled
CI / Test downstream esphome/device-builder (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.14) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.14) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.14) (push) Has been cancelled
CI / Determine which jobs to run (push) Has been cancelled
CI / Run integration tests (${{ matrix.bucket.name }}) (push) Has been cancelled
CI / Run C++ unit tests (push) Has been cancelled
CI / Run CodSpeed benchmarks (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Test components batch (${{ matrix.components }}) (push) Has been cancelled
CI / Test components with native ESP-IDF (push) Has been cancelled
CI / pre-commit.ci lite (push) Has been cancelled
CI / Build target branch for memory impact (push) Has been cancelled
CI / Build PR branch for memory impact (push) Has been cancelled
CI / Comment memory impact (push) Has been cancelled
CI / CI Status (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04-arm) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04-arm) (push) Has been cancelled

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
luar123
2026-05-11 17:54:35 +02:00
committed by GitHub
parent a52ca4f80a
commit ee8ca2a3bf
9 changed files with 51 additions and 29 deletions
+11 -6
View File
@@ -79,10 +79,7 @@ CONFIG_SCHEMA = cv.All(
cv.string, cv.Length(max=31)
),
cv.Optional(CONF_ROUTER, default=False): cv.boolean,
cv.Optional(CONF_ON_JOIN): cv.All(
cv.requires_component("nrf52"),
automation.validate_automation(single=True),
),
cv.Optional(CONF_ON_JOIN): automation.validate_automation({}),
cv.OnlyWith(CONF_WIPE_ON_BOOT, "nrf52", default=False): cv.All(
cv.Any(
cv.boolean,
@@ -146,17 +143,25 @@ FINAL_VALIDATE_SCHEMA = cv.All(
)
_CALLBACK_AUTOMATIONS = [
automation.CallbackAutomation(CONF_ON_JOIN, "add_on_join_callback", [(bool, "x")]),
]
@coroutine_with_priority(CoroPriority.CORE)
async def to_code(config: ConfigType) -> None:
cg.add_define("USE_ZIGBEE")
var = None
if CORE.using_zephyr:
from .zigbee_zephyr import zephyr_to_code
await zephyr_to_code(config)
var = await zephyr_to_code(config)
if CORE.is_esp32:
from .zigbee_esp32 import esp32_to_code
await esp32_to_code(config)
var = await esp32_to_code(config)
if var is not None:
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
async def setup_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
@@ -26,7 +26,7 @@ void ZigbeeTime::setup() {
global_time = this;
this->parent_->add_callback(this->endpoint_, [this](zb_bufid_t bufid) { this->zcl_device_cb_(bufid); });
synchronize_epoch_(EPOCH_2000);
this->parent_->add_join_callback([this]() { zb_zcl_time_server_synchronize(this->endpoint_, sync_time); });
this->parent_->add_on_join_callback([this](bool x) { zb_zcl_time_server_synchronize(this->endpoint_, sync_time); });
}
void ZigbeeTime::dump_config() {
+14 -2
View File
@@ -59,11 +59,13 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
ESP_LOGD(TAG, "Device started up in %sfactory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non ");
global_zigbee->started = true;
if (esp_zb_bdb_is_factory_new()) {
global_zigbee->factory_new = true;
ESP_LOGD(TAG, "Start network steering");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGD(TAG, "Device rebooted");
global_zigbee->connected = true;
global_zigbee->joined = true;
global_zigbee->enable_loop_soon_any_context();
}
} else {
ESP_LOGE(TAG, "FIRST_START. Device started up in %sfactory-reset mode with an error %d (%s)",
@@ -78,7 +80,8 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
steering_retry_count = 0;
ESP_LOGI(TAG, "Joined network successfully (PAN ID: 0x%04hx, Channel:%d)", esp_zb_get_pan_id(),
esp_zb_get_current_channel());
global_zigbee->connected = true;
global_zigbee->joined = true;
global_zigbee->enable_loop_soon_any_context();
} else {
ESP_LOGI(TAG, "Network steering was not successful (status: %s)", esp_err_to_name(err_status));
if (steering_retry_count < 10) {
@@ -283,6 +286,15 @@ void ZigbeeComponent::setup() {
}
}
xTaskCreate(esp_zb_task_, "Zigbee_main", 4096, NULL, 24, NULL);
this->disable_loop(); // loop is only needed for processing events, so disable until we join a network
}
void ZigbeeComponent::loop() {
if (this->joined.exchange(false)) {
this->connected_ = true;
this->join_cb_.call(this->factory_new);
}
this->disable_loop();
}
void ZigbeeComponent::dump_config() {
+8 -2
View File
@@ -39,6 +39,7 @@ class ZigbeeAttribute;
class ZigbeeComponent : public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
esp_err_t create_endpoint(uint8_t endpoint_id, zb_ha_standard_devs_e device_id,
esp_zb_cluster_list_t *esp_zb_cluster_list);
@@ -59,10 +60,13 @@ class ZigbeeComponent : public Component {
esp_zb_lock_release();
}
template<typename F> void add_on_join_callback(F &&cb) { this->join_cb_.add(std::forward<F>(cb)); }
bool is_started() { return this->started; }
bool is_connected() { return this->connected; }
std::atomic<bool> connected = false;
bool is_connected() { return this->connected_; }
std::atomic<bool> started = false;
std::atomic<bool> joined = false;
std::atomic<bool> factory_new = false;
protected:
struct {
@@ -70,6 +74,7 @@ class ZigbeeComponent : public Component {
uint8_t *manufacturer;
uint8_t *date;
} basic_cluster_data_;
bool connected_ = false;
#ifdef ZB_ED_ROLE
esp_zb_nwk_device_type_t device_role_ = ESP_ZB_DEVICE_TYPE_ED;
#else
@@ -89,6 +94,7 @@ class ZigbeeComponent : 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<std::tuple<uint8_t, uint16_t, uint8_t, uint16_t>, ZigbeeAttribute *> attributes_;
esp_zb_ep_list_t *esp_zb_ep_list_ = esp_zb_ep_list_create();
CallbackManager<void(bool)> join_cb_{};
};
extern "C" void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
+3 -1
View File
@@ -28,6 +28,7 @@ from esphome.const import (
)
from esphome.core import CORE
from esphome.coroutine import CoroPriority, coroutine_with_priority
from esphome.cpp_generator import MockObj
import esphome.final_validate as fv
from esphome.types import ConfigType
@@ -289,7 +290,7 @@ async def attributes_to_code(
cg.add(attr_var.connect(template_arg, device))
async def esp32_to_code(config: ConfigType) -> None:
async def esp32_to_code(config: ConfigType) -> "MockObj":
add_idf_component(
name="espressif/esp-zboss-lib",
ref="1.6.4",
@@ -332,3 +333,4 @@ async def esp32_to_code(config: ConfigType) -> None:
)
)
await attributes_to_code(var, ep[CONF_NUM], cl)
return var
+5 -6
View File
@@ -40,7 +40,7 @@ void ZigbeeComponent::zboss_signal_handler_esphome(zb_bufid_t bufid) {
case ZB_BDB_SIGNAL_DEVICE_REBOOT:
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_REBOOT, status: %d", status);
if (status == RET_OK) {
on_join_();
on_join_(false);
}
break;
case ZB_BDB_SIGNAL_STEERING:
@@ -88,7 +88,7 @@ void ZigbeeComponent::zboss_signal_handler_esphome(zb_bufid_t bufid) {
for (int i = 0; i < addr_len; ++i) {
if (ieee_addr_buf[i] != '0') {
on_join_();
on_join_(true);
break;
}
}
@@ -130,11 +130,10 @@ void ZigbeeComponent::zcl_device_cb(zb_bufid_t bufid) {
p_device_cb_param->status = RET_NOT_IMPLEMENTED;
}
void ZigbeeComponent::on_join_() {
this->defer([this]() {
void ZigbeeComponent::on_join_(bool factory_new) {
this->defer([this, factory_new]() {
ESP_LOGD(TAG, "Joined the network");
this->join_trigger_.trigger();
this->join_cb_.call();
this->join_cb_.call(factory_new);
});
}
+3 -5
View File
@@ -74,25 +74,23 @@ class ZigbeeComponent : public Component {
// endpoints are enumerated from 1
this->callbacks_[endpoint - 1] = std::move(cb);
}
template<typename F> void add_join_callback(F &&cb) { this->join_cb_.add(std::forward<F>(cb)); }
template<typename F> void add_on_join_callback(F &&cb) { this->join_cb_.add(std::forward<F>(cb)); }
void zboss_signal_handler_esphome(zb_bufid_t bufid);
void after_reporting_info(zb_zcl_configure_reporting_req_t *config_rep_req, zb_zcl_attr_addr_info_t *attr_addr_info);
void factory_reset();
Trigger<> *get_join_trigger() { return &this->join_trigger_; };
void force_report();
void loop() override;
void set_sleepy(bool sleepy) { this->sleepy_ = sleepy; }
protected:
static void zcl_device_cb(zb_bufid_t bufid);
void on_join_();
void on_join_(bool factory_new);
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
void erase_flash_(int area);
#endif
void dump_reporting_();
std::array<std::function<void(zb_bufid_t bufid)>, ZIGBEE_ENDPOINTS_COUNT> callbacks_{};
CallbackManager<void()> join_cb_;
Trigger<> join_trigger_;
CallbackManager<void(bool)> join_cb_;
bool force_report_{false};
uint32_t sleep_time_{};
uint32_t sleep_remainder_{};
+3 -6
View File
@@ -1,7 +1,6 @@
import datetime
import random
from esphome import automation
import esphome.codegen as cg
from esphome.components.zephyr import zephyr_add_prj_conf
import esphome.config_validation as cv
@@ -23,7 +22,6 @@ from esphome.types import ConfigType
from .const import (
BACNET_UNIT_NO_UNITS,
BACNET_UNITS,
CONF_ON_JOIN,
CONF_POWER_SOURCE,
CONF_ROUTER,
CONF_WIPE_ON_BOOT,
@@ -96,7 +94,7 @@ zephyr_number = cv.Schema(
)
async def zephyr_to_code(config: ConfigType) -> None:
async def zephyr_to_code(config: ConfigType) -> "MockObj":
zephyr_add_prj_conf("ZIGBEE", True)
zephyr_add_prj_conf("ZIGBEE_APP_UTILS", True)
if config[CONF_ROUTER]:
@@ -141,15 +139,14 @@ async def zephyr_to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
if on_join_config := config.get(CONF_ON_JOIN):
await automation.build_automation(var.get_join_trigger(), [], on_join_config)
await cg.register_component(var, config)
CORE.add_job(_ctx_to_code, config)
cg.add(var.set_sleepy(config[CONF_SLEEPY]))
return var
async def _attr_to_code(config: ConfigType) -> None:
# Create the basic attributes structure and attribute list
@@ -12,3 +12,6 @@ binary_sensor:
zigbee:
model: zigbee_test
router: true
on_join:
then:
- logger.log: "Joined network"