mirror of
https://github.com/esphome/esphome.git
synced 2026-05-23 21:30:28 +08:00
[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
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:
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user