mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 06:36:23 +00:00
[zigbee] cleanup, docstrings, refactor connected state on esp32 (1/3) (#18007)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI
parent
6bcbdd79c3
commit
2d5d34d33f
@@ -9,16 +9,18 @@ namespace esphome::zigbee {
|
|||||||
static const char *const TAG = "zigbee.attribute";
|
static const char *const TAG = "zigbee.attribute";
|
||||||
|
|
||||||
void ZigbeeAttribute::set_attr_() {
|
void ZigbeeAttribute::set_attr_() {
|
||||||
if (!this->zb_->is_connected()) {
|
if (!this->zb_->is_started()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) {
|
if (esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) {
|
||||||
ezb_zcl_status_t state = ezb_zcl_set_attr_value(this->endpoint_id_, this->cluster_id_, this->role_, this->attr_id_,
|
ezb_zcl_status_t state = ezb_zcl_set_attr_value(this->endpoint_id_, this->cluster_id_, this->role_, this->attr_id_,
|
||||||
EZB_ZCL_STD_MANUF_CODE, this->value_p_, false);
|
EZB_ZCL_STD_MANUF_CODE, this->value_p_, false);
|
||||||
|
// cleared before report_() so it can disable the loop
|
||||||
|
// when the report has to wait for join
|
||||||
|
this->set_attr_requested_ = false;
|
||||||
if (this->force_report_) {
|
if (this->force_report_) {
|
||||||
this->report_(true);
|
this->report_(true);
|
||||||
}
|
}
|
||||||
this->set_attr_requested_ = false;
|
|
||||||
// Check for error
|
// Check for error
|
||||||
if (state != EZB_ZCL_STATUS_SUCCESS) {
|
if (state != EZB_ZCL_STATUS_SUCCESS) {
|
||||||
ESP_LOGE(TAG, "Setting attribute failed, ZCL status: %u", static_cast<unsigned>(state));
|
ESP_LOGE(TAG, "Setting attribute failed, ZCL status: %u", static_cast<unsigned>(state));
|
||||||
@@ -28,7 +30,14 @@ void ZigbeeAttribute::set_attr_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeAttribute::report_(bool has_lock) {
|
void ZigbeeAttribute::report_(bool has_lock) {
|
||||||
if (!this->zb_->is_connected() || !this->report_enabled) {
|
if (!this->report_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->zb_->is_joined()) {
|
||||||
|
this->report_requested_ = true;
|
||||||
|
if (!this->set_attr_requested_) {
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (has_lock or esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) {
|
if (has_lock or esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) {
|
||||||
@@ -44,6 +53,7 @@ void ZigbeeAttribute::report_(bool has_lock) {
|
|||||||
cmd.payload.attr_id = this->attr_id_;
|
cmd.payload.attr_id = this->attr_id_;
|
||||||
|
|
||||||
ezb_zcl_report_attr_cmd_req(&cmd);
|
ezb_zcl_report_attr_cmd_req(&cmd);
|
||||||
|
this->report_requested_ = false;
|
||||||
if (!has_lock) {
|
if (!has_lock) {
|
||||||
esp_zigbee_lock_release();
|
esp_zigbee_lock_release();
|
||||||
}
|
}
|
||||||
@@ -55,6 +65,11 @@ void ZigbeeAttribute::set_report(ZigbeeReportT report) {
|
|||||||
if (report == ZigbeeReportT::ZIGBEE_REPORT_FORCE) {
|
if (report == ZigbeeReportT::ZIGBEE_REPORT_FORCE) {
|
||||||
this->force_report_ = true;
|
this->force_report_ = true;
|
||||||
}
|
}
|
||||||
|
this->zb_->add_on_join_callback([this](bool) {
|
||||||
|
if (this->report_requested_) {
|
||||||
|
this->enable_loop();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeAttribute::loop() {
|
void ZigbeeAttribute::loop() {
|
||||||
@@ -62,7 +77,11 @@ void ZigbeeAttribute::loop() {
|
|||||||
this->set_attr_();
|
this->set_attr_();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->set_attr_requested_) {
|
if (this->report_requested_) {
|
||||||
|
this->report_(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->report_requested_ && !this->set_attr_requested_) {
|
||||||
this->disable_loop();
|
this->disable_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class ZigbeeAttribute final : public Component {
|
|||||||
float scale_;
|
float scale_;
|
||||||
void *value_p_{nullptr};
|
void *value_p_{nullptr};
|
||||||
bool set_attr_requested_{false};
|
bool set_attr_requested_{false};
|
||||||
|
bool report_requested_{false};
|
||||||
bool force_report_{false};
|
bool force_report_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ ep_configs: dict[str, dict[str, Any]] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_next_ep_num(eps: list[int]) -> int:
|
def _get_next_ep_num(eps: list[int]) -> int:
|
||||||
try:
|
try:
|
||||||
ep_num = [i for i in range(1, CONF_MAX_EP_NUMBER + 1) if i not in eps][0]
|
ep_num = [i for i in range(1, CONF_MAX_EP_NUMBER + 1) if i not in eps][0]
|
||||||
eps.append(ep_num)
|
eps.append(ep_num)
|
||||||
@@ -94,7 +94,7 @@ def get_next_ep_num(eps: list[int]) -> int:
|
|||||||
return ep_num
|
return ep_num
|
||||||
|
|
||||||
|
|
||||||
def compare_clusters(
|
def _compare_clusters(
|
||||||
existing_ep: dict[str, Any],
|
existing_ep: dict[str, Any],
|
||||||
ep: dict[str, Any],
|
ep: dict[str, Any],
|
||||||
) -> tuple[str | int, str] | None:
|
) -> tuple[str | int, str] | None:
|
||||||
@@ -105,12 +105,12 @@ def compare_clusters(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def merge_endpoints(
|
def _merge_endpoints(
|
||||||
existing_ep: dict[str, Any],
|
existing_ep: dict[str, Any],
|
||||||
ep: dict[str, Any],
|
ep: dict[str, Any],
|
||||||
use_type: bool | None,
|
use_type: bool | None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if compare_clusters(existing_ep, ep):
|
if _compare_clusters(existing_ep, ep):
|
||||||
return False
|
return False
|
||||||
if (
|
if (
|
||||||
ep.get(DEVICE_TYPE)
|
ep.get(DEVICE_TYPE)
|
||||||
@@ -134,7 +134,12 @@ def merge_endpoints(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def validate_endpoints(ep_dict: dict[int, dict]) -> None:
|
def _validate_endpoints(ep_dict: dict[int, dict]) -> None:
|
||||||
|
"""Validate endpoint device type selection before endpoint creation.
|
||||||
|
|
||||||
|
This resolves any deferred device type selections stored in CONF_USE_DEVICE_TYPE,
|
||||||
|
ensuring each endpoint has at most one active device type.
|
||||||
|
"""
|
||||||
for num, ep in ep_dict.items():
|
for num, ep in ep_dict.items():
|
||||||
types_dict = ep.get(CONF_USE_DEVICE_TYPE)
|
types_dict = ep.get(CONF_USE_DEVICE_TYPE)
|
||||||
if not types_dict:
|
if not types_dict:
|
||||||
@@ -157,10 +162,18 @@ def validate_endpoints(ep_dict: dict[int, dict]) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def create_ep(router: bool) -> None:
|
def create_ep(router: bool) -> None:
|
||||||
|
"""Finalize Zigbee endpoint creation and normalize endpoint storage.
|
||||||
|
|
||||||
|
Validate endpoints, merge endpoints, and assign numbers to endpoints without an explicit number.
|
||||||
|
This is called from final_validate.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
router: Whether the device is acting as a Zigbee router.
|
||||||
|
"""
|
||||||
zb_data = CORE.data.setdefault(KEY_ZIGBEE, {})
|
zb_data = CORE.data.setdefault(KEY_ZIGBEE, {})
|
||||||
ep_dict: dict[int, dict] = zb_data.setdefault(KEY_ZIGBEE_EP, {})
|
ep_dict: dict[int, dict] = zb_data.setdefault(KEY_ZIGBEE_EP, {})
|
||||||
ep_list: list[dict] = zb_data.setdefault(KEY_ZIGBEE_EP_NO_NUM, [])
|
ep_list: list[dict] = zb_data.setdefault(KEY_ZIGBEE_EP_NO_NUM, [])
|
||||||
validate_endpoints(ep_dict)
|
_validate_endpoints(ep_dict)
|
||||||
# create dummy endpoint if list is empty
|
# create dummy endpoint if list is empty
|
||||||
if not ep_dict and not ep_list:
|
if not ep_dict and not ep_list:
|
||||||
ep_type = "CUSTOM_ATTR"
|
ep_type = "CUSTOM_ATTR"
|
||||||
@@ -173,7 +186,7 @@ def create_ep(router: bool) -> None:
|
|||||||
for ep in ep_list:
|
for ep in ep_list:
|
||||||
added = False
|
added = False
|
||||||
for existing_ep in ep_list_new:
|
for existing_ep in ep_list_new:
|
||||||
if merge_endpoints(existing_ep, ep, ep.get(CONF_USE_DEVICE_TYPE)):
|
if _merge_endpoints(existing_ep, ep, ep.get(CONF_USE_DEVICE_TYPE)):
|
||||||
added = True
|
added = True
|
||||||
break
|
break
|
||||||
if not added:
|
if not added:
|
||||||
@@ -182,7 +195,7 @@ def create_ep(router: bool) -> None:
|
|||||||
# Add endpoints with no number to the endpoint dict with a new number
|
# Add endpoints with no number to the endpoint dict with a new number
|
||||||
eps = list(ep_dict.keys())
|
eps = list(ep_dict.keys())
|
||||||
for ep in ep_list_new:
|
for ep in ep_list_new:
|
||||||
ep_num = get_next_ep_num(eps)
|
ep_num = _get_next_ep_num(eps)
|
||||||
ep_dict[ep_num] = ep
|
ep_dict[ep_num] = ep
|
||||||
|
|
||||||
# clear list so that it is not processed again
|
# clear list so that it is not processed again
|
||||||
@@ -195,6 +208,14 @@ def create_ep(router: bool) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def add_ep(ep: dict[str, Any], ep_num: int | None, use_type: bool | None) -> None:
|
def add_ep(ep: dict[str, Any], ep_num: int | None, use_type: bool | None) -> None:
|
||||||
|
"""Add a Zigbee endpoint configuration to CORE.data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ep: Endpoint configuration dictionary.
|
||||||
|
ep_num: Optional explicit endpoint number.
|
||||||
|
use_type: Optional boolean indicating whether this component's device type should be
|
||||||
|
used for the endpoint (True claims it, False drops it, None leaves it as a candidate).
|
||||||
|
"""
|
||||||
zb_data = CORE.data.setdefault(KEY_ZIGBEE, {})
|
zb_data = CORE.data.setdefault(KEY_ZIGBEE, {})
|
||||||
if use_type is False:
|
if use_type is False:
|
||||||
ep.pop(DEVICE_TYPE, None)
|
ep.pop(DEVICE_TYPE, None)
|
||||||
@@ -208,7 +229,7 @@ def add_ep(ep: dict[str, Any], ep_num: int | None, use_type: bool | None) -> Non
|
|||||||
if ep_num in ep_dict:
|
if ep_num in ep_dict:
|
||||||
# check if the existing endpoint has same clusters
|
# check if the existing endpoint has same clusters
|
||||||
existing_ep = ep_dict[ep_num]
|
existing_ep = ep_dict[ep_num]
|
||||||
if cl := compare_clusters(
|
if cl := _compare_clusters(
|
||||||
existing_ep,
|
existing_ep,
|
||||||
ep,
|
ep,
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) {
|
|||||||
switch (signal_type) {
|
switch (signal_type) {
|
||||||
case EZB_ZDO_SIGNAL_SKIP_STARTUP:
|
case EZB_ZDO_SIGNAL_SKIP_STARTUP:
|
||||||
ESP_LOGD(TAG, "Zigbee stack initialized");
|
ESP_LOGD(TAG, "Zigbee stack initialized");
|
||||||
|
global_zigbee->started = true;
|
||||||
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION);
|
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION);
|
||||||
break;
|
break;
|
||||||
case EZB_BDB_SIGNAL_DEVICE_FIRST_START:
|
case EZB_BDB_SIGNAL_DEVICE_FIRST_START:
|
||||||
@@ -60,7 +61,6 @@ bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) {
|
|||||||
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *) ezb_app_signal_get_params(app_signal));
|
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *) ezb_app_signal_get_params(app_signal));
|
||||||
if (status == EZB_BDB_STATUS_SUCCESS) {
|
if (status == EZB_BDB_STATUS_SUCCESS) {
|
||||||
ESP_LOGD(TAG, "Device started up in %sfactory-reset mode", ezb_bdb_is_factory_new() ? "" : "non ");
|
ESP_LOGD(TAG, "Device started up in %sfactory-reset mode", ezb_bdb_is_factory_new() ? "" : "non ");
|
||||||
global_zigbee->started = true;
|
|
||||||
if (ezb_bdb_is_factory_new()) {
|
if (ezb_bdb_is_factory_new()) {
|
||||||
global_zigbee->factory_new = true;
|
global_zigbee->factory_new = true;
|
||||||
ESP_LOGD(TAG, "Start network steering");
|
ESP_LOGD(TAG, "Start network steering");
|
||||||
@@ -303,9 +303,10 @@ void ZigbeeComponent::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeComponent::loop() {
|
void ZigbeeComponent::loop() {
|
||||||
if (this->joined.exchange(false)) {
|
if (!this->join_reported_ && this->joined) {
|
||||||
this->connected_ = true;
|
this->join_reported_ = true;
|
||||||
this->join_cb_.call(this->factory_new);
|
this->join_cb_.call(this->factory_new);
|
||||||
|
this->factory_new = false;
|
||||||
}
|
}
|
||||||
this->disable_loop();
|
this->disable_loop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,8 +63,13 @@ class ZigbeeComponent final : public Component {
|
|||||||
template<typename F> void add_on_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)); }
|
||||||
|
|
||||||
bool is_battery_powered() { return this->basic_cluster_data_.power_source == EZB_ZCL_BASIC_POWER_SOURCE_BATTERY; }
|
bool is_battery_powered() { return this->basic_cluster_data_.power_source == EZB_ZCL_BASIC_POWER_SOURCE_BATTERY; }
|
||||||
|
|
||||||
|
// 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; }
|
||||||
bool is_connected() { return this->connected_; }
|
|
||||||
|
// True if the device has joined a network and is ready to send and receive messages.
|
||||||
|
bool is_joined() { return this->joined; }
|
||||||
std::atomic<bool> started = false;
|
std::atomic<bool> started = false;
|
||||||
std::atomic<bool> joined = false;
|
std::atomic<bool> joined = false;
|
||||||
std::atomic<bool> factory_new = false;
|
std::atomic<bool> factory_new = false;
|
||||||
@@ -76,7 +81,6 @@ class ZigbeeComponent final : public Component {
|
|||||||
uint8_t *date;
|
uint8_t *date;
|
||||||
uint8_t power_source;
|
uint8_t power_source;
|
||||||
} basic_cluster_data_;
|
} basic_cluster_data_;
|
||||||
bool connected_ = false;
|
|
||||||
#ifdef CONFIG_ZB_ZED
|
#ifdef CONFIG_ZB_ZED
|
||||||
ezb_nwk_device_type_t device_role_ = EZB_NWK_DEVICE_TYPE_END_DEVICE;
|
ezb_nwk_device_type_t device_role_ = EZB_NWK_DEVICE_TYPE_END_DEVICE;
|
||||||
#else
|
#else
|
||||||
@@ -91,6 +95,7 @@ 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
|
// 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_;
|
std::map<std::tuple<uint8_t, uint16_t, uint8_t, uint16_t>, ZigbeeAttribute *> attributes_;
|
||||||
ezb_af_device_desc_t dev_desc_;
|
ezb_af_device_desc_t dev_desc_;
|
||||||
|
bool join_reported_{false};
|
||||||
CallbackManager<void(bool)> join_cb_{};
|
CallbackManager<void(bool)> join_cb_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user