[zigbee] Bump zigbee sdk to 2.0.2 (#16869)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
luar123
2026-07-03 20:28:29 +02:00
committed by GitHub
parent 187cd51867
commit 7ad43358c2
15 changed files with 358 additions and 366 deletions
+16
View File
@@ -8,6 +8,9 @@ from esphome.components.esp32.const import (
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32H4,
VARIANT_ESP32H21,
VARIANT_ESP32S31,
)
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MODEL, CONF_NAME
@@ -52,11 +55,21 @@ CODEOWNERS = ["@luar123", "@tomaszduda23"]
CONFLICTS_WITH = ["openthread"]
def _check_report_deprecation(value: str) -> str:
if str(value).lower() in ("coordinator", "enable"):
_LOGGER.warning(
"Report options 'coordinator' and 'enable' are deprecated and will be removed in a future release. Use 'default' instead."
)
return value
BASE_SCHEMA = cv.Schema(
{
cv.Optional(CONF_REPORT): cv.All(
cv.requires_component("zigbee"),
cv.requires_component("esp32"),
_check_report_deprecation,
cv.enum(REPORT, lower=True),
)
}
@@ -111,7 +124,10 @@ CONFIG_SCHEMA = cv.All(
cv.only_on_esp32,
only_on_variant(
supported=[
VARIANT_ESP32S31,
VARIANT_ESP32H2,
VARIANT_ESP32H21,
VARIANT_ESP32H4,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
]
+8 -7
View File
@@ -55,6 +55,7 @@ REPORT = {
"coordinator": report.ZIGBEE_REPORT_COORDINATOR,
"enable": report.ZIGBEE_REPORT_ENABLE,
"force": report.ZIGBEE_REPORT_FORCE,
"default": report.ZIGBEE_REPORT_DEFAULT,
}
CONF_ON_JOIN = "on_join"
@@ -63,13 +64,13 @@ CONF_REPORT = "report"
CONF_ROUTER = "router"
CONF_POWER_SOURCE = "power_source"
POWER_SOURCE = {
"UNKNOWN": "ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN",
"MAINS_SINGLE_PHASE": "ZB_ZCL_BASIC_POWER_SOURCE_MAINS_SINGLE_PHASE",
"MAINS_THREE_PHASE": "ZB_ZCL_BASIC_POWER_SOURCE_MAINS_THREE_PHASE",
"BATTERY": "ZB_ZCL_BASIC_POWER_SOURCE_BATTERY",
"DC_SOURCE": "ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE",
"EMERGENCY_MAINS_CONST": "ZB_ZCL_BASIC_POWER_SOURCE_EMERGENCY_MAINS_CONST",
"EMERGENCY_MAINS_TRANSF": "ZB_ZCL_BASIC_POWER_SOURCE_EMERGENCY_MAINS_TRANSF",
"UNKNOWN": 0x00, # ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN
"MAINS_SINGLE_PHASE": 0x01, # ZB_ZCL_BASIC_POWER_SOURCE_MAINS_SINGLE_PHASE
"MAINS_THREE_PHASE": 0x02, # ZB_ZCL_BASIC_POWER_SOURCE_MAINS_THREE_PHASE
"BATTERY": 0x03, # ZB_ZCL_BASIC_POWER_SOURCE_BATTERY
"DC_SOURCE": 0x04, # ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE
"EMERGENCY_MAINS_CONST": 0x05, # ZB_ZCL_BASIC_POWER_SOURCE_EMERGENCY_MAINS_CONST
"EMERGENCY_MAINS_TRANSF": 0x06, # ZB_ZCL_BASIC_POWER_SOURCE_EMERGENCY_MAINS_TRANSF
}
KEY_ZIGBEE = "zigbee"
+14 -16
View File
@@ -13,27 +13,25 @@ CONF_ATTRIBUTE_ID = "attribute_id"
KEY_BS_EP = "binary_sensor_ep"
KEY_SENSOR_EP = "sensor_ep"
ha_standard_devices = cg.esphome_ns.enum("zb_ha_standard_devs_e")
DEVICE_ID = {
"RANGE_EXTENDER": ha_standard_devices.ZB_HA_RANGE_EXTENDER_DEVICE_ID,
"SIMPLE_SENSOR": ha_standard_devices.ZB_HA_SIMPLE_SENSOR_DEVICE_ID,
"CUSTOM_ATTR": ha_standard_devices.ZB_HA_CUSTOM_ATTR_DEVICE_ID,
"RANGE_EXTENDER": cg.RawExpression("EZB_ZHA_RANGE_EXTENDER_DEVICE_ID"),
"SIMPLE_SENSOR": cg.RawExpression("EZB_ZHA_SIMPLE_SENSOR_DEVICE_ID"),
"CUSTOM_ATTR": 0xFFF2,
}
cluster_id = cg.esphome_ns.enum("esp_zb_zcl_cluster_id_t")
cluster_id = cg.esphome_ns.enum("ezb_zcl_cluster_id_e")
CLUSTER_ID = {
"BASIC": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BASIC,
"BINARY_INPUT": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT,
"ANALOG_INPUT": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
"BASIC": cluster_id.EZB_ZCL_CLUSTER_ID_BASIC,
"BINARY_INPUT": cluster_id.EZB_ZCL_CLUSTER_ID_BINARY_INPUT,
"ANALOG_INPUT": cluster_id.EZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
}
cluster_role = cg.esphome_ns.enum("esp_zb_zcl_cluster_role_t")
CLUSTER_ROLE = {
"SERVER": cluster_role.ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
"SERVER": cg.RawExpression("EZB_ZCL_CLUSTER_SERVER"),
}
attr_type = cg.esphome_ns.enum("esp_zb_zcl_attr_type_t")
attr_type = cg.esphome_ns.enum("ezb_zcl_attr_type_e")
ATTR_TYPE = {
"BOOL": attr_type.ESP_ZB_ZCL_ATTR_TYPE_BOOL,
"8BITMAP": attr_type.ESP_ZB_ZCL_ATTR_TYPE_8BITMAP,
"CHAR_STRING": attr_type.ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING,
"SINGLE": attr_type.ESP_ZB_ZCL_ATTR_TYPE_SINGLE,
"DOUBLE": attr_type.ESP_ZB_ZCL_ATTR_TYPE_DOUBLE,
"BOOL": attr_type.EZB_ZCL_ATTR_TYPE_BOOL,
"MAP8": attr_type.EZB_ZCL_ATTR_TYPE_MAP8,
"STRING": attr_type.EZB_ZCL_ATTR_TYPE_STRING,
"SINGLE": attr_type.EZB_ZCL_ATTR_TYPE_SINGLE,
"DOUBLE": attr_type.EZB_ZCL_ATTR_TYPE_DOUBLE,
}
@@ -12,63 +12,68 @@ void ZigbeeAttribute::set_attr_() {
if (!this->zb_->is_connected()) {
return;
}
if (esp_zb_lock_acquire(10 / portTICK_PERIOD_MS)) {
esp_zb_zcl_status_t state = esp_zb_zcl_set_attribute_val(this->endpoint_id_, this->cluster_id_, this->role_,
this->attr_id_, this->value_p_, false);
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_STD_MANUF_CODE, this->value_p_, false);
if (this->force_report_) {
this->report_(true);
}
this->set_attr_requested_ = false;
// Check for error
if (state != ESP_ZB_ZCL_STATUS_SUCCESS) {
if (state != EZB_ZCL_STATUS_SUCCESS) {
ESP_LOGE(TAG, "Setting attribute failed, ZCL status: %u", static_cast<unsigned>(state));
}
esp_zb_lock_release();
esp_zigbee_lock_release();
}
}
void ZigbeeAttribute::report_(bool has_lock) {
if (!this->zb_->is_connected()) {
if (!this->zb_->is_connected() || !this->report_enabled) {
return;
}
if (has_lock or esp_zb_lock_acquire(10 / portTICK_PERIOD_MS)) {
esp_zb_zcl_report_attr_cmd_t cmd = {};
cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
cmd.zcl_basic_cmd.dst_addr_u.addr_short = 0x0000;
cmd.zcl_basic_cmd.dst_endpoint = 1;
cmd.zcl_basic_cmd.src_endpoint = this->endpoint_id_;
cmd.clusterID = this->cluster_id_;
cmd.attributeID = this->attr_id_;
if (has_lock or esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) {
ezb_zcl_report_attr_cmd_t cmd = {};
cmd.cmd_ctrl.fc.direction = EZB_ZCL_CMD_DIRECTION_TO_CLI;
cmd.cmd_ctrl.fc.dis_default_rsp = 1;
cmd.cmd_ctrl.dst_addr.addr_mode = EZB_ADDR_MODE_SHORT;
cmd.cmd_ctrl.dst_addr.u.short_addr = 0x0000;
cmd.cmd_ctrl.dst_ep = 1;
cmd.cmd_ctrl.src_ep = this->endpoint_id_;
cmd.cmd_ctrl.cluster_id = this->cluster_id_;
cmd.cmd_ctrl.fc.manuf_specific = 0;
cmd.payload.attr_id = this->attr_id_;
esp_zb_zcl_report_attr_cmd_req(&cmd);
ezb_zcl_report_attr_cmd_req(&cmd);
if (!has_lock) {
esp_zb_lock_release();
esp_zigbee_lock_release();
}
}
}
esp_zb_zcl_reporting_info_t ZigbeeAttribute::get_reporting_info() {
esp_zb_zcl_reporting_info_t reporting_info = {};
reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
reporting_info.ep = this->endpoint_id_;
reporting_info.cluster_id = this->cluster_id_;
reporting_info.cluster_role = this->role_;
reporting_info.attr_id = this->attr_id_;
reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
reporting_info.u.send_info.min_interval = 10; /*!< Actual minimum reporting interval */
reporting_info.u.send_info.max_interval = 0; /*!< Actual maximum reporting interval */
reporting_info.u.send_info.def_min_interval = 10; /*!< Default minimum reporting interval */
reporting_info.u.send_info.def_max_interval = 0; /*!< Default maximum reporting interval */
reporting_info.u.send_info.delta.s16 = 0; /*!< Actual reportable change */
return reporting_info;
void ZigbeeAttribute::setup_reporting() {
ezb_zcl_reporting_info_t reporting_info = ezb_zcl_reporting_info_find(
this->endpoint_id_, this->cluster_id_, this->role_, this->attr_id_, EZB_ZCL_STD_MANUF_CODE);
if (reporting_info == EZB_ZCL_INVALID_REPORTING_INFO) {
ESP_LOGD(TAG, "Could not find reporting info for attribute 0x%04X in cluster 0x%04X in endpoint %u", this->attr_id_,
this->cluster_id_, this->endpoint_id_);
this->report_enabled = false;
this->force_report_ = false;
} else {
ESP_LOGD(TAG, "Found reporting info for attr 0x%04X in cluster 0x%04X", this->attr_id_, this->cluster_id_);
ezb_zcl_attr_variable_t delta = {.u64 = 0};
ezb_zcl_reporting_info_update_default_interval(reporting_info, 0, 65000);
ezb_zcl_reporting_info_update(reporting_info, 0, 65000, &delta);
if (ezb_zcl_reporting_start_attr_report(reporting_info) != EZB_ERR_NONE) {
ESP_LOGE(TAG, "Could not start reporting for attribute");
}
}
}
void ZigbeeAttribute::set_report(bool force) {
void ZigbeeAttribute::set_report(ZigbeeReportT report) {
this->report_enabled = true;
this->force_report_ = force;
if (report == ZigbeeReportT::ZIGBEE_REPORT_FORCE) {
this->force_report_ = true;
}
}
void ZigbeeAttribute::loop() {
@@ -9,7 +9,7 @@
#ifdef USE_ESP32
#ifdef USE_ZIGBEE
#include "esp_zigbee_core.h"
#include "esp_zigbee.h"
#include "zigbee_esp32.h"
#ifdef USE_SENSOR
@@ -22,6 +22,7 @@
namespace esphome::zigbee {
enum ZigbeeReportT {
ZIGBEE_REPORT_DEFAULT,
ZIGBEE_REPORT_COORDINATOR,
ZIGBEE_REPORT_ENABLE,
ZIGBEE_REPORT_FORCE,
@@ -41,10 +42,10 @@ class ZigbeeAttribute final : public Component {
scale_(scale) {}
void loop() override;
template<typename T> void add_attr(T value);
esp_zb_zcl_reporting_info_t get_reporting_info();
void setup_reporting();
template<typename T> void set_attr(const T &value);
uint8_t attr_type() { return attr_type_; }
void set_report(bool force);
void set_report(ZigbeeReportT report);
#ifdef USE_SENSOR
template<typename T> void connect(sensor::Sensor *sensor);
#endif
+6 -6
View File
@@ -27,7 +27,7 @@ ep_configs: dict[str, dict[str, Any]] = {
{
CONF_ATTRIBUTE_ID: 0x55,
CONF_TYPE: "BOOL",
CONF_REPORT: REPORT["enable"],
CONF_REPORT: REPORT["default"],
CONF_DEVICE: None,
},
{
@@ -36,11 +36,11 @@ ep_configs: dict[str, dict[str, Any]] = {
},
{
CONF_ATTRIBUTE_ID: 0x6F,
CONF_TYPE: "8BITMAP",
CONF_TYPE: "MAP8",
},
{
CONF_ATTRIBUTE_ID: 0x1C,
CONF_TYPE: "CHAR_STRING",
CONF_TYPE: "STRING",
},
],
},
@@ -56,7 +56,7 @@ ep_configs: dict[str, dict[str, Any]] = {
{
CONF_ATTRIBUTE_ID: 0x55,
CONF_TYPE: "SINGLE",
CONF_REPORT: REPORT["enable"],
CONF_REPORT: REPORT["default"],
CONF_DEVICE: None,
},
{
@@ -65,11 +65,11 @@ ep_configs: dict[str, dict[str, Any]] = {
},
{
CONF_ATTRIBUTE_ID: 0x6F,
CONF_TYPE: "8BITMAP",
CONF_TYPE: "MAP8",
},
{
CONF_ATTRIBUTE_ID: 0x1C,
CONF_TYPE: "CHAR_STRING",
CONF_TYPE: "STRING",
},
],
},
+182 -171
View File
@@ -36,121 +36,143 @@ uint8_t *get_zcl_string(const char *str, uint8_t max_size, bool use_max_size) {
return zcl_str;
}
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {
if (esp_zb_bdb_start_top_level_commissioning(mode_mask) != ESP_OK) {
ESP_LOGE(TAG, "Start network steering failed!");
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); });
return;
}
if (ezb_bdb_start_top_level_commissioning(mode) != EZB_ERR_NONE) {
ESP_LOGE(TAG, "Start top level commissioning failed!");
}
esp_zigbee_lock_release();
}
extern "C" void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
bool ZigbeeComponent::app_signal_handler(const ezb_app_signal_t *app_signal) {
static uint8_t steering_retry_count = 0;
uint32_t *p_sg_p = signal_struct->p_app_signal;
esp_err_t err_status = signal_struct->esp_err_status;
esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t) *p_sg_p;
esp_zb_zdo_signal_leave_params_t *leave_params = NULL;
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
ezb_app_signal_type_t signal_type = ezb_app_signal_get_type(app_signal);
switch (signal_type) {
case EZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGD(TAG, "Zigbee stack initialized");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
if (ezb_bdb_is_factory_new()) {
global_zigbee->defer([]() { global_zigbee->setup_reporting(); });
} else {
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION);
}
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
if (err_status == ESP_OK) {
ESP_LOGD(TAG, "Device started up in %sfactory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non ");
case EZB_BDB_SIGNAL_DEVICE_FIRST_START:
case EZB_BDB_SIGNAL_DEVICE_REBOOT: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *) ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Device started up in %sfactory-reset mode", ezb_bdb_is_factory_new() ? "" : "non ");
global_zigbee->started = true;
if (esp_zb_bdb_is_factory_new()) {
if (ezb_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);
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGD(TAG, "Device rebooted");
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)",
esp_zb_bdb_is_factory_new() ? "" : "non ", err_status, esp_err_to_name(err_status));
ESP_LOGW(TAG, "Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
esp_zb_scheduler_alarm((esp_zb_callback_t) bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_INITIALIZATION,
1000);
ESP_LOGW(TAG, "The %s failed with status(0x%02x), please retry", ezb_app_signal_to_string(signal_type), status);
global_zigbee->set_timeout("zb_init", 1000, []() {
ZigbeeComponent::esp_zigbee_alarm_bdb_commissioning(EZB_BDB_MODE_INITIALIZATION);
});
}
break;
case ESP_ZB_BDB_SIGNAL_STEERING:
if (err_status == ESP_OK) {
} break;
case EZB_BDB_SIGNAL_STEERING: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *) ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
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());
ezb_extpanid_t extended_pan_id;
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->enable_loop_soon_any_context();
} else {
ESP_LOGI(TAG, "Network steering was not successful (status: %s)", esp_err_to_name(err_status));
ESP_LOGD(TAG, "Failed to join network with status(0x%02x)", status);
if (steering_retry_count < 10) {
steering_retry_count++;
esp_zb_scheduler_alarm((esp_zb_callback_t) bdb_start_top_level_commissioning_cb,
ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
global_zigbee->set_timeout("zb_init", 1000, []() {
ZigbeeComponent::esp_zigbee_alarm_bdb_commissioning(EZB_BDB_MODE_NETWORK_STEERING);
});
} else {
esp_zb_scheduler_alarm((esp_zb_callback_t) bdb_start_top_level_commissioning_cb,
ESP_ZB_BDB_MODE_NETWORK_STEERING, 600 * 1000);
global_zigbee->set_timeout("zb_init", 600 * 1000, []() {
ZigbeeComponent::esp_zigbee_alarm_bdb_commissioning(EZB_BDB_MODE_NETWORK_STEERING);
});
}
}
break;
case ESP_ZB_ZDO_SIGNAL_LEAVE:
leave_params = (esp_zb_zdo_signal_leave_params_t *) esp_zb_app_signal_get_params(p_sg_p);
if (leave_params->leave_type == ESP_ZB_NWK_LEAVE_TYPE_RESET) {
esp_zb_factory_reset();
} break;
case EZB_ZDO_SIGNAL_LEAVE: {
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();
}
break;
} break;
default:
ESP_LOGD(TAG, "ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
ESP_LOGD(TAG, "Zigbee APP Signal: %s(type: 0x%02x)", ezb_app_signal_to_string(signal_type), signal_type);
break;
}
return true;
}
static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) {
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(message, ESP_FAIL, TAG, "Empty message");
ESP_RETURN_ON_FALSE(message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG,
"Received message: error status(%d)", message->info.status);
ESP_LOGD(TAG, "Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)",
message->info.dst_endpoint, message->info.cluster, message->attribute.id, message->attribute.data.size);
return ret;
static void zb_attribute_handler(ezb_zcl_set_attr_value_message_t *message) {
ESP_RETURN_ON_FALSE(message, , TAG, "Empty message");
ESP_RETURN_ON_FALSE(message->info.status == EZB_ZCL_STATUS_SUCCESS, , TAG, "Received message: error status(%d)",
message->info.status);
ESP_LOGD(TAG, "ZCL SetAttributeValue message for endpoint(%d) cluster(0x%04x) %s with status(0x%02x)",
message->info.dst_ep, message->info.cluster_id,
message->info.cluster_role == EZB_ZCL_CLUSTER_SERVER ? "server" : "client", message->info.status);
}
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) {
esp_err_t ret = ESP_OK;
static void zb_action_handler(ezb_zcl_core_action_callback_id_t callback_id, void *message) {
switch (callback_id) {
case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:
ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *) message);
case EZB_ZCL_CORE_SET_ATTR_VALUE_CB_ID:
zb_attribute_handler((ezb_zcl_set_attr_value_message_t *) message);
break;
#ifdef ESPHOME_LOG_HAS_VERBOSE
case EZB_ZCL_CORE_DEFAULT_RSP_CB_ID: {
ezb_zcl_cmd_default_rsp_message_t *default_rsp = (ezb_zcl_cmd_default_rsp_message_t *) message;
ESP_LOGV(TAG, "Received ZCL Default Response: 0x%02x", default_rsp->in.status_code);
} break;
#endif
default:
ESP_LOGD(TAG, "Receive Zigbee action(0x%x) callback", callback_id);
ESP_LOGD(TAG, "Receive Zigbee action(0x%04x) callback", static_cast<unsigned>(callback_id));
break;
}
return ret;
}
void ZigbeeComponent::create_default_cluster(uint8_t endpoint_id, zb_ha_standard_devs_e device_id) {
esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
this->endpoint_list_[endpoint_id] =
std::tuple<zb_ha_standard_devs_e, esp_zb_cluster_list_t *>(device_id, cluster_list);
// Add basic cluster
this->add_cluster(endpoint_id, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
// Add identify cluster if not already present
if (esp_zb_cluster_list_get_cluster(cluster_list, ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE) ==
nullptr) {
this->add_cluster(endpoint_id, ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
void ZigbeeComponent::create_default_cluster(uint8_t endpoint_id, uint16_t device_id) {
ezb_af_ep_config_t config = {
.ep_id = endpoint_id,
.app_profile_id = EZB_AF_HA_PROFILE_ID,
.app_device_id = device_id,
.app_device_version = 0,
};
ezb_af_ep_desc_t ep_desc = ezb_af_create_endpoint_desc(&config);
if (ezb_af_device_add_endpoint_desc(this->dev_desc_, ep_desc) != EZB_ERR_NONE) {
ESP_LOGE(TAG, "Could not create endpoint %u", endpoint_id);
}
// Add basic cluster
this->update_basic_cluster_(ep_desc);
// Add identify cluster if not already present
this->add_cluster(endpoint_id, EZB_ZCL_CLUSTER_ID_IDENTIFY, EZB_ZCL_CLUSTER_SERVER);
}
void ZigbeeComponent::add_cluster(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role) {
esp_zb_attribute_list_t *attr_list;
if (cluster_id == 0) {
attr_list = create_basic_cluster_();
} else {
attr_list = esphome_zb_default_attr_list_create(cluster_id);
if (cluster_id == EZB_ZCL_CLUSTER_ID_BASIC) {
return;
}
this->attribute_list_[{endpoint_id, cluster_id, role}] = attr_list;
ezb_af_ep_desc_t ep_desc = ezb_af_device_get_endpoint_desc(this->dev_desc_, endpoint_id);
if (ep_desc == NULL) {
ESP_LOGE(TAG, "Endpoint %u does not exist, cannot add cluster 0x%04X", endpoint_id, cluster_id);
return;
}
esphome_zb_add_or_update_cluster(cluster_id, ep_desc, role);
ESP_LOGD(TAG, "Endpoint %u: Added cluster 0x%04X with role %u", endpoint_id, cluster_id, role);
}
void ZigbeeComponent::set_basic_cluster(const char *model, const char *manufacturer, uint8_t power_source) {
@@ -166,131 +188,117 @@ void ZigbeeComponent::set_basic_cluster(const char *model, const char *manufactu
};
}
esp_zb_attribute_list_t *ZigbeeComponent::create_basic_cluster_() {
esp_zb_basic_cluster_cfg_t basic_cluster_cfg = {
.zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE,
.power_source = this->basic_cluster_data_.power_source,
};
esp_zb_attribute_list_t *attr_list = esp_zb_basic_cluster_create(&basic_cluster_cfg);
esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID,
this->basic_cluster_data_.manufacturer);
esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, this->basic_cluster_data_.model);
esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_DATE_CODE_ID, this->basic_cluster_data_.date);
return attr_list;
void ZigbeeComponent::update_basic_cluster_(ezb_af_ep_desc_t ep_desc) {
ezb_zcl_cluster_desc_t cluster_desc =
ezb_af_endpoint_get_cluster_desc(ep_desc, EZB_ZCL_CLUSTER_ID_BASIC, EZB_ZCL_CLUSTER_SERVER);
if (cluster_desc == NULL) {
ezb_zcl_basic_cluster_config_t basic_cluster_cfg = {
.zcl_version = EZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE,
.power_source = this->basic_cluster_data_.power_source,
};
cluster_desc = ezb_zcl_basic_create_cluster_desc(&basic_cluster_cfg, EZB_ZCL_CLUSTER_SERVER);
}
ezb_zcl_basic_cluster_desc_add_attr(cluster_desc, EZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID,
this->basic_cluster_data_.manufacturer);
ezb_zcl_basic_cluster_desc_add_attr(cluster_desc, EZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID,
this->basic_cluster_data_.model);
ezb_zcl_basic_cluster_desc_add_attr(cluster_desc, EZB_ZCL_ATTR_BASIC_DATE_CODE_ID, this->basic_cluster_data_.date);
ezb_af_endpoint_add_cluster_desc(ep_desc, cluster_desc);
}
esp_err_t ZigbeeComponent::create_endpoint(uint8_t endpoint_id, zb_ha_standard_devs_e device_id,
esp_zb_cluster_list_t *esp_zb_cluster_list) {
esp_zb_endpoint_config_t endpoint_config = {.endpoint = endpoint_id,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = static_cast<uint16_t>(device_id),
.app_device_version = 0};
return esp_zb_ep_list_add_ep(this->esp_zb_ep_list_, esp_zb_cluster_list, endpoint_config);
void ZigbeeComponent::setup_reporting() {
ESP_LOGD(TAG, "Setting up reporting for all attributes");
esp_zigbee_lock_acquire(portMAX_DELAY);
for (auto &[_, attribute] : this->attributes_) {
attribute->setup_reporting();
}
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION);
esp_zigbee_lock_release();
}
static void esp_zb_task(void *pv_parameters) {
if (esp_zb_start(false) != ESP_OK) {
static void ezb_task(void *pv_parameters) {
if (esp_zigbee_start(false) != ESP_OK) {
ESP_LOGE(TAG, "Could not setup Zigbee");
vTaskDelete(NULL);
}
if (global_zigbee->is_battery_powered()) {
ESP_LOGD(TAG, "Battery powered!");
esp_zb_set_node_descriptor_power_source(false);
} else {
esp_zb_set_node_descriptor_power_source(true);
esp_zigbee_launch_mainloop();
esp_zigbee_deinit();
vTaskDelete(NULL);
}
ZigbeeComponent::ZigbeeComponent() {
esp_zigbee_platform_config_t platform_config = {
.storage_partition_name = "nvs",
.radio_config = EZB_DEFAULT_RADIO_CONFIG(),
};
esp_zigbee_device_config_t device_config = {
.device_type = this->device_role_,
.install_code_policy = false,
};
#ifdef CONFIG_ZB_ZCZR
esp_zigbee_zczr_config_s zb_zczr_cfg = {
.max_children = MAX_CHILDREN,
};
device_config.zczr_config = zb_zczr_cfg;
#else
esp_zigbee_zed_config_s zb_zed_cfg = {
.ed_timeout = EZB_NWK_ED_TIMEOUT_64MIN,
.keep_alive = ED_KEEP_ALIVE,
};
device_config.zed_config = zb_zed_cfg;
#endif
esp_zigbee_config_t config = {.device_config = device_config, .platform_config = platform_config};
if (esp_zigbee_init(&config) != ESP_OK) {
ESP_LOGE(TAG, "Could not initialize Zigbee");
this->mark_failed();
return;
}
esp_zb_stack_main_loop();
this->dev_desc_ = ezb_af_create_device_desc();
}
void ZigbeeComponent::setup() {
global_zigbee = this;
esp_zb_platform_config_t config = {};
config.radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG();
config.host_config = ESP_ZB_DEFAULT_HOST_CONFIG();
#ifdef USE_WIFI
if (esp_coex_wifi_i154_enable() != ESP_OK) {
this->mark_failed();
return;
}
#endif
if (esp_zb_platform_config(&config) != ESP_OK) {
ezb_aps_secur_enable_distributed_security(false);
ezb_nwk_set_min_join_lqi(32);
if (ezb_app_signal_add_handler(ZigbeeComponent::app_signal_handler) != ESP_OK) {
ESP_LOGE(TAG, "Could not set application signal handler");
this->mark_failed();
return;
}
esp_zb_cfg_t zb_nwk_cfg = {
.esp_zb_role = this->device_role_,
.install_code_policy = false,
};
#ifdef ZB_ROUTER_ROLE
esp_zb_zczr_cfg_t zb_zczr_cfg = {
.max_children = MAX_CHILDREN,
};
zb_nwk_cfg.nwk_cfg.zczr_cfg = zb_zczr_cfg;
#else
esp_zb_zed_cfg_t zb_zed_cfg = {
.ed_timeout = ESP_ZB_ED_AGING_TIMEOUT_64MIN,
.keep_alive = ED_KEEP_ALIVE,
};
zb_nwk_cfg.nwk_cfg.zed_cfg = zb_zed_cfg;
#endif
esp_zb_init(&zb_nwk_cfg);
esp_err_t ret;
for (auto const &[key, val] : this->attribute_list_) {
esp_zb_cluster_list_t *esp_zb_cluster_list = std::get<1>(this->endpoint_list_[std::get<0>(key)]);
ret = esphome_zb_cluster_list_add_or_update_cluster(std::get<1>(key), esp_zb_cluster_list, val, std::get<2>(key));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Could not create cluster 0x%04X with role %u: %s", std::get<1>(key), std::get<2>(key),
esp_err_to_name(ret));
} else {
ESP_LOGD(TAG, "Endpoint %u: Added cluster 0x%04X with role %u", std::get<0>(key), std::get<1>(key),
std::get<2>(key));
#ifdef ESPHOME_LOG_HAS_VERBOSE
// Dump cluster attributes in verbose log
ESP_LOGV(TAG, "Cluster 0x%04X attributes:", std::get<1>(key));
esp_zb_attribute_list_t *attr_list = val;
while (attr_list) {
esp_zb_zcl_attr_t *attr = &attr_list->attribute;
ESP_LOGV(TAG, " Attr ID: 0x%04X, Type: 0x%02X, Access: 0x%02X", attr->id, attr->type, attr->access);
attr_list = attr_list->next;
}
#endif
}
}
this->attribute_list_.clear();
for (auto const &[ep_id, dev_id] : this->endpoint_list_) {
if (create_endpoint(ep_id, std::get<0>(dev_id), std::get<1>(dev_id)) != ESP_OK) {
ESP_LOGE(TAG, "Could not create endpoint %u", ep_id);
}
}
this->endpoint_list_.clear();
if (esp_zb_device_register(this->esp_zb_ep_list_) != ESP_OK) {
if (ezb_af_device_desc_register(this->dev_desc_) != EZB_ERR_NONE) {
ESP_LOGE(TAG, "Could not register the endpoint list");
this->mark_failed();
return;
}
esp_zb_core_action_handler_register(zb_action_handler);
ezb_zcl_core_action_handler_register(zb_action_handler);
if (esp_zb_set_primary_network_channel_set(ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK) != ESP_OK) {
if (ezb_bdb_set_primary_channel_set(EZB_PRIMARY_CHANNEL_MASK) != ESP_OK) {
ESP_LOGE(TAG, "Could not setup Zigbee");
this->mark_failed();
return;
}
for (auto &[_, attribute] : this->attributes_) {
if (attribute->report_enabled) {
esp_zb_zcl_reporting_info_t reporting_info = attribute->get_reporting_info();
ESP_LOGD(TAG, "set reporting for cluster: %u", reporting_info.cluster_id);
if (esp_zb_zcl_update_reporting_info(&reporting_info) != ESP_OK) {
ESP_LOGE(TAG, "Could not configure reporting for attribute 0x%04X in cluster 0x%04X in endpoint %u",
reporting_info.attr_id, reporting_info.cluster_id, reporting_info.ep);
}
}
}
xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 24, NULL);
uint8_t power_source = static_cast<uint8_t>(this->is_battery_powered() ? EZB_AF_NODE_POWER_SOURCE_RECHARGEABLE_BATTERY
: EZB_AF_NODE_POWER_SOURCE_CONSTANT_POWER);
ezb_af_node_power_desc_t desc = {
.current_power_mode = EZB_AF_NODE_POWER_MODE_SYNC_ON_WHEN_IDLE,
.available_power_sources = power_source,
.current_power_source = power_source,
.current_power_source_level = EZB_AF_NODE_POWER_SOURCE_LEVEL_100_PERCENT,
};
ezb_af_set_node_power_desc(&desc);
xTaskCreate(ezb_task, "Zigbee_main", 4096, NULL, 24, NULL);
this->disable_loop(); // loop is only needed for processing events, so disable until we join a network
}
@@ -303,25 +311,28 @@ void ZigbeeComponent::loop() {
}
void ZigbeeComponent::dump_config() {
if (esp_zb_lock_acquire(10 / portTICK_PERIOD_MS)) {
if (esp_zigbee_lock_acquire(10 / portTICK_PERIOD_MS)) {
ESP_LOGCONFIG(TAG,
"Zigbee\n"
" Model: %s\n"
" Model: %.*s\n"
" Router: %s\n"
" Device is joined to the network: %s\n"
" Current channel: %d\n"
" Short addr: 0x%04X\n"
" Short pan id: 0x%04X",
this->basic_cluster_data_.model, YESNO(this->device_role_ == ESP_ZB_DEVICE_TYPE_ROUTER),
YESNO(esp_zb_bdb_dev_joined()), esp_zb_get_current_channel(), esp_zb_get_short_address(),
esp_zb_get_pan_id());
esp_zb_lock_release();
this->basic_cluster_data_.model[0],
reinterpret_cast<const char *>(this->basic_cluster_data_.model + 1),
YESNO(this->device_role_ == EZB_NWK_DEVICE_TYPE_ROUTER), YESNO(ezb_bdb_dev_joined()),
ezb_nwk_get_current_channel(), ezb_nwk_get_short_address(), ezb_nwk_get_panid());
esp_zigbee_lock_release();
} else {
ESP_LOGCONFIG(TAG,
"Zigbee\n"
" Model: %s\n"
" Model: %.*s\n"
" Router: %s\n",
this->basic_cluster_data_.model, YESNO(this->device_role_ == ESP_ZB_DEVICE_TYPE_ROUTER));
this->basic_cluster_data_.model[0],
reinterpret_cast<const char *>(this->basic_cluster_data_.model + 1),
YESNO(this->device_role_ == EZB_NWK_DEVICE_TYPE_ROUTER));
}
}
} // namespace esphome::zigbee
+30 -27
View File
@@ -8,9 +8,8 @@
#include <tuple>
#include <atomic>
#include "esp_zigbee_core.h"
#include "zboss_api.h"
#include "ha/esp_zigbee_ha_standard.h"
#include "esp_zigbee.h"
#include "ezbee/zha.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "zigbee_helpers_esp32.h"
@@ -24,12 +23,10 @@ namespace esphome::zigbee {
/* Zigbee configuration */
static const uint16_t ED_KEEP_ALIVE = 3000; /* 3000 millisecond */
static const uint8_t MAX_CHILDREN = 10;
static const uint32_t EZB_PRIMARY_CHANNEL_MASK = 0x07FFF800U; /* channels 11-26 */
#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
{ .radio_mode = ZB_RADIO_MODE_NATIVE, }
#define ESP_ZB_DEFAULT_HOST_CONFIG() \
{ .host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, }
#define EZB_DEFAULT_RADIO_CONFIG() \
{ .radio_mode = ESP_ZIGBEE_RADIO_MODE_NATIVE, }
uint8_t *get_zcl_string(const char *str, uint8_t max_size, bool use_max_size = false);
@@ -37,14 +34,15 @@ class ZigbeeAttribute;
class ZigbeeComponent final : public Component {
public:
ZigbeeComponent();
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);
void set_basic_cluster(const char *model, const char *manufacturer, uint8_t power_source);
void add_cluster(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role);
void create_default_cluster(uint8_t endpoint_id, zb_ha_standard_devs_e device_id);
void create_default_cluster(uint8_t endpoint_id, uint16_t device_id);
void setup_reporting();
template<typename T>
void add_attr(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,
@@ -53,15 +51,18 @@ class ZigbeeComponent final : public Component {
template<typename T>
void add_attr(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id, uint8_t max_size, T value);
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_zb_lock_acquire(portMAX_DELAY);
esp_zb_factory_reset(); // triggers a reboot
esp_zb_lock_release();
esp_zigbee_lock_acquire(portMAX_DELAY);
esp_zigbee_factory_reset(); // triggers a reboot
esp_zigbee_lock_release();
}
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 == ESP_ZB_ZCL_BASIC_POWER_SOURCE_BATTERY; }
bool is_battery_powered() { return this->basic_cluster_data_.power_source == EZB_ZCL_BASIC_POWER_SOURCE_BATTERY; }
bool is_started() { return this->started; }
bool is_connected() { return this->connected_; }
std::atomic<bool> started = false;
@@ -76,25 +77,20 @@ class ZigbeeComponent final : public Component {
uint8_t power_source;
} basic_cluster_data_;
bool connected_ = false;
#ifdef ZB_ED_ROLE
esp_zb_nwk_device_type_t device_role_ = ESP_ZB_DEVICE_TYPE_ED;
#ifdef CONFIG_ZB_ZED
ezb_nwk_device_type_t device_role_ = EZB_NWK_DEVICE_TYPE_END_DEVICE;
#else
esp_zb_nwk_device_type_t device_role_ = ESP_ZB_DEVICE_TYPE_ROUTER;
ezb_nwk_device_type_t device_role_ = EZB_NWK_DEVICE_TYPE_ROUTER;
#endif
esp_zb_attribute_list_t *create_basic_cluster_();
void update_basic_cluster_(ezb_af_ep_desc_t ep_desc);
template<typename T>
void add_attr_(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,
T *value_p);
// endpoint_list_ and attribute_list_ are only used during setup and are cleared afterwards
// value tuple could be replaced by struct
std::map<uint8_t, std::tuple<zb_ha_standard_devs_e, esp_zb_cluster_list_t *>> endpoint_list_;
// key tuple could be replaced by single 32 bit int with bit fields for endpoint, cluster and role
std::map<std::tuple<uint8_t, uint16_t, uint8_t>, esp_zb_attribute_list_t *> attribute_list_;
// attributes_ will be used during operation in zigbee callbacks to update the attribute values and trigger
// automations
// 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();
ezb_af_device_desc_t dev_desc_;
CallbackManager<void(bool)> join_cb_{};
};
@@ -125,8 +121,15 @@ void ZigbeeComponent::add_attr(ZigbeeAttribute *attr, uint8_t endpoint_id, uint1
template<typename T>
void ZigbeeComponent::add_attr_(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role,
uint16_t attr_id, T *value_p) {
esp_zb_attribute_list_t *attr_list = this->attribute_list_[{endpoint_id, cluster_id, role}];
esphome_zb_cluster_add_or_update_attr(cluster_id, attr_list, attr_id, value_p);
ezb_af_ep_desc_t ep_desc = ezb_af_device_get_endpoint_desc(this->dev_desc_, endpoint_id);
if (ep_desc == NULL) {
return;
}
ezb_zcl_cluster_desc_t cluster_desc = ezb_af_endpoint_get_cluster_desc(ep_desc, cluster_id, role);
if (cluster_desc == NULL) {
return;
}
esphome_zb_cluster_add_or_update_attr(cluster_id, cluster_desc, attr_id, value_p);
if (attr != nullptr) {
this->attributes_[{endpoint_id, cluster_id, role, attr_id}] = attr;
+9 -27
View File
@@ -9,7 +9,6 @@ from esphome.components.esp32 import (
add_idf_component,
add_idf_sdkconfig_option,
add_partition,
require_libc_picolibc_newlib_compat,
require_vfs_select,
)
import esphome.config_validation as cv
@@ -41,7 +40,6 @@ from .const import (
CONF_ROUTER,
KEY_ZIGBEE,
POWER_SOURCE,
REPORT,
ZigbeeAttribute,
)
from .const_esp32 import (
@@ -76,7 +74,7 @@ def get_c_type(attr_type: str) -> Any | None:
return cg.double
if "STRING" in attr_type:
return cg.std_string
test = re.match(r"(^U?)(\d{1,2})(BITMAP$|BIT$|BIT_ENUM$|$)", attr_type)
test = re.match(r"^(DATA|UINT|MAP|ENUM)(\d{1,2})$", attr_type)
if test and test.group(2):
return getattr(cg, "uint" + get_c_size(test.group(2), [8, 16, 32, 64]))
return None
@@ -89,14 +87,14 @@ def get_cv_by_type(attr_type: str) -> Any | None:
return cv.float_
if "STRING" in attr_type:
return cv.string
test = re.match(r"(^U?)(\d{1,2})(BITMAP$|BIT$|BIT_ENUM$|$)", attr_type)
test = re.match(r"^(DATA|UINT|MAP|ENUM)(\d{1,2})$", attr_type)
if test and test.group(2):
return cv.positive_int
raise cv.Invalid(f"Zigbee: type {attr_type} not supported or implemented")
def get_default_by_type(attr_type: str) -> str | bool | int | float:
if attr_type == "CHAR_STRING":
if attr_type == "STRING":
return ""
if attr_type == "BOOL":
return False
@@ -134,7 +132,6 @@ def final_validate_esp32(config: ConfigType) -> ConfigType:
) as f:
partitions_tab = f.read()
for partition, types in [
("zb_storage", {"type": "data", "subtype": "fat", "size": 0x4000}),
("zb_fct", {"type": "data", "subtype": "fat", "size": 0x1000}),
]:
if partition not in partitions_tab:
@@ -191,14 +188,14 @@ def validate_sensor_esp32(config: ConfigType) -> ConfigType:
{
CONF_ATTRIBUTE_ID: 0x100,
CONF_VALUE: (apptype << 16) | 0xFFFF,
CONF_TYPE: "U32",
CONF_TYPE: "UINT32",
},
)
ep[CONF_CLUSTERS][0][CONF_ATTRIBUTES].append(
{
CONF_ATTRIBUTE_ID: 0x75,
CONF_VALUE: bacunit,
CONF_TYPE: "16BIT_ENUM",
CONF_TYPE: "ENUM16",
},
)
setup_attributes(config, ep[CONF_CLUSTERS])
@@ -233,15 +230,8 @@ async def _zigbee_add_sdkconfigs(config: ConfigType) -> None:
add_idf_sdkconfig_option("CONFIG_ZB_ZCZR", True)
else:
add_idf_sdkconfig_option("CONFIG_ZB_ZED", True)
add_idf_sdkconfig_option("CONFIG_ZB_RADIO_NATIVE", True)
if CONF_WIFI in CORE.config:
add_idf_sdkconfig_option("CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE", 4096)
# The pre-built Zigbee library uses esp_log_default_level which requires
# dynamic log level control to be enabled
add_idf_sdkconfig_option("CONFIG_LOG_DYNAMIC_LEVEL_CONTROL", True)
# The pre-built Zigbee library is compiled against newlib which requires newlib
# reentrancy to be enabled with picolibc compatibility (IDF 6.0+ only).
require_libc_picolibc_newlib_compat()
async def attributes_to_code(
@@ -274,11 +264,8 @@ async def attributes_to_code(
await cg.register_component(attr_var, attr)
cg.add(attr_var.add_attr(attr[CONF_VALUE]))
if CONF_REPORT in attr and attr[CONF_REPORT] in [
REPORT["enable"],
REPORT["force"],
]:
cg.add(attr_var.set_report(attr[CONF_REPORT] == REPORT["force"]))
if CONF_REPORT in attr:
cg.add(attr_var.set_report(attr[CONF_REPORT]))
if CONF_DEVICE in attr:
device = await cg.get_variable(attr[CONF_DEVICE])
@@ -287,20 +274,15 @@ async def attributes_to_code(
async def esp32_to_code(config: ConfigType) -> "MockObj":
add_idf_component(
name="espressif/esp-zboss-lib",
ref="1.6.4",
)
add_idf_component(
name="espressif/esp-zigbee-lib",
ref="1.6.8",
ref="2.0.2",
)
# add sdkconfigs later so they can overwrite esp32 defaults
CORE.add_job(_zigbee_add_sdkconfigs, config)
# add partitions for zigbee
add_partition("zb_storage", "data", "fat", 0x4000) # 16KB
add_partition("zb_fct", "data", "fat", 0x1000) # 4KB, minimum size
# create endpoints
@@ -316,7 +298,7 @@ async def esp32_to_code(config: ConfigType) -> "MockObj":
var.set_basic_cluster(
config[CONF_MODEL],
"esphome",
cg.RawExpression(POWER_SOURCE[config[CONF_POWER_SOURCE]]),
POWER_SOURCE[config[CONF_POWER_SOURCE]],
)
)
for ep in ep_list:
@@ -2,78 +2,59 @@
#ifdef USE_ESP32
#ifdef USE_ZIGBEE
#include "ha/esp_zigbee_ha_standard.h"
#include "zigbee_helpers_esp32.h"
#include "ezbee/zha.h"
esp_err_t esphome_zb_cluster_add_or_update_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list,
ezb_err_t esphome_zb_cluster_add_or_update_attr(uint16_t cluster_id, ezb_zcl_cluster_desc_t cluster_desc,
uint16_t attr_id, void *value_p) {
esp_err_t ret;
ret = esp_zb_cluster_update_attr(attr_list, attr_id, value_p);
if (ret != ESP_OK) {
ESP_LOGE("zigbee_helper", "Ignore previous attribute not found error");
ret = esphome_zb_cluster_add_attr(cluster_id, attr_list, attr_id, value_p);
ezb_zcl_attr_desc_t attr_desc = ezb_zcl_cluster_get_attr_desc(cluster_desc, attr_id, EZB_ZCL_STD_MANUF_CODE);
if (attr_desc != NULL) {
return ezb_zcl_attr_desc_set_value(attr_desc, value_p);
}
if (ret != ESP_OK) {
ESP_LOGE("zigbee_helper", "Could not add attribute 0x%04X to cluster 0x%04X: %s", attr_id, cluster_id,
esp_err_to_name(ret));
}
return ret;
return esphome_zb_cluster_add_attr(cluster_id, cluster_desc, attr_id, value_p);
}
esp_err_t esphome_zb_cluster_list_add_or_update_cluster(uint16_t cluster_id, esp_zb_cluster_list_t *cluster_list,
esp_zb_attribute_list_t *attr_list, uint8_t role_mask) {
esp_err_t ret;
ret = esp_zb_cluster_list_update_cluster(cluster_list, attr_list, cluster_id, role_mask);
if (ret != ESP_OK) {
ESP_LOGE("zigbee_helper", "Ignore previous cluster not found error");
switch (cluster_id) {
case ESP_ZB_ZCL_CLUSTER_ID_BASIC:
ret = esp_zb_cluster_list_add_basic_cluster(cluster_list, attr_list, role_mask);
break;
case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY:
ret = esp_zb_cluster_list_add_identify_cluster(cluster_list, attr_list, role_mask);
break;
case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
ret = esp_zb_cluster_list_add_analog_input_cluster(cluster_list, attr_list, role_mask);
break;
case ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT:
ret = esp_zb_cluster_list_add_binary_input_cluster(cluster_list, attr_list, role_mask);
break;
default:
ret = esp_zb_cluster_list_add_custom_cluster(cluster_list, attr_list, role_mask);
ezb_err_t esphome_zb_add_or_update_cluster(uint16_t cluster_id, ezb_af_ep_desc_t ep_desc, uint8_t role_mask) {
if (ezb_af_endpoint_get_cluster_desc(ep_desc, cluster_id, role_mask) != NULL) {
// Cluster already exists, nothing to do
return EZB_ERR_NONE;
}
ezb_zcl_cluster_desc_t cluster_desc;
cluster_desc = esphome_zb_default_cluster_dscr_create(cluster_id, role_mask);
return ezb_af_endpoint_add_cluster_desc(ep_desc, cluster_desc);
}
ezb_zcl_cluster_desc_t esphome_zb_default_cluster_dscr_create(uint16_t cluster_id, uint8_t role_mask) {
switch (cluster_id) {
case EZB_ZCL_CLUSTER_ID_BASIC:
return ezb_zcl_basic_create_cluster_desc(NULL, role_mask);
case EZB_ZCL_CLUSTER_ID_IDENTIFY:
return ezb_zcl_identify_create_cluster_desc(NULL, role_mask);
case EZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
return ezb_zcl_analog_input_create_cluster_desc(NULL, role_mask);
case EZB_ZCL_CLUSTER_ID_BINARY_INPUT:
return ezb_zcl_binary_input_create_cluster_desc(NULL, role_mask);
default: {
ezb_zcl_custom_cluster_config_t config = {0};
config.cluster_id = cluster_id;
return ezb_zcl_custom_create_cluster_desc(&config, role_mask);
}
}
return ret;
}
esp_zb_attribute_list_t *esphome_zb_default_attr_list_create(uint16_t cluster_id) {
switch (cluster_id) {
case ESP_ZB_ZCL_CLUSTER_ID_BASIC:
return esp_zb_basic_cluster_create(NULL);
case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY:
return esp_zb_identify_cluster_create(NULL);
case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
return esp_zb_analog_input_cluster_create(NULL);
case ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT:
return esp_zb_binary_input_cluster_create(NULL);
default:
return esp_zb_zcl_attr_list_create(cluster_id);
}
}
esp_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list, uint16_t attr_id,
ezb_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, ezb_zcl_cluster_desc_t cluster_desc, uint16_t attr_id,
void *value_p) {
switch (cluster_id) {
case ESP_ZB_ZCL_CLUSTER_ID_BASIC:
return esp_zb_basic_cluster_add_attr(attr_list, attr_id, value_p);
case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY:
return esp_zb_identify_cluster_add_attr(attr_list, attr_id, value_p);
case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
return esp_zb_analog_input_cluster_add_attr(attr_list, attr_id, value_p);
case ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT:
return esp_zb_binary_input_cluster_add_attr(attr_list, attr_id, value_p);
case EZB_ZCL_CLUSTER_ID_BASIC:
return ezb_zcl_basic_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
case EZB_ZCL_CLUSTER_ID_IDENTIFY:
return ezb_zcl_identify_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
case EZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
return ezb_zcl_analog_input_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
case EZB_ZCL_CLUSTER_ID_BINARY_INPUT:
return ezb_zcl_binary_input_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
default:
return ESP_FAIL;
return EZB_ERR_NOT_FOUND;
}
}
@@ -8,15 +8,14 @@
extern "C" {
#endif
#include "esp_zigbee_core.h"
#include "esp_zigbee.h"
esp_err_t esphome_zb_cluster_list_add_or_update_cluster(uint16_t cluster_id, esp_zb_cluster_list_t *cluster_list,
esp_zb_attribute_list_t *attr_list, uint8_t role_mask);
esp_zb_attribute_list_t *esphome_zb_default_attr_list_create(uint16_t cluster_id);
esp_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list, uint16_t attr_id,
void *value_p);
esp_err_t esphome_zb_cluster_add_or_update_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list,
ezb_err_t esphome_zb_cluster_add_or_update_attr(uint16_t cluster_id, ezb_zcl_cluster_desc_t cluster_desc,
uint16_t attr_id, void *value_p);
ezb_err_t esphome_zb_add_or_update_cluster(uint16_t cluster_id, ezb_af_ep_desc_t ep_desc, uint8_t role_mask);
ezb_zcl_cluster_desc_t esphome_zb_default_cluster_dscr_create(uint16_t cluster_id, uint8_t role_mask);
ezb_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, ezb_zcl_cluster_desc_t cluster_desc, uint16_t attr_id,
void *value_p);
#ifdef __cplusplus
}
+1 -1
View File
@@ -168,7 +168,7 @@ async def _attr_to_code(config: ConfigType) -> None:
),
zigbee_assign(
basic_attrs.power_source,
cg.RawExpression(POWER_SOURCE[config[CONF_POWER_SOURCE]]),
POWER_SOURCE[config[CONF_POWER_SOURCE]],
),
zigbee_set_string(basic_attrs.location_id, ""),
zigbee_assign(
+1 -5
View File
@@ -47,12 +47,8 @@ dependencies:
version: "2.0.0"
rules:
- if: "target in [esp32, esp32p4]"
espressif/esp-zboss-lib:
version: 1.6.4
rules:
- if: "target in [esp32h2, esp32c5, esp32c6]"
espressif/esp-zigbee-lib:
version: 1.6.8
version: 2.0.2
rules:
- if: "target in [esp32h2, esp32c5, esp32c6]"
espressif/lan87xx:
-1
View File
@@ -11,4 +11,3 @@ CONFIG_OPENTHREAD_RADIO_NATIVE=y
# zigbee
CONFIG_ZB_ENABLED=y
CONFIG_ZB_ZED=y
CONFIG_ZB_RADIO_NATIVE=y
+1 -1
View File
@@ -4,7 +4,7 @@ packages:
binary_sensor:
- platform: template
name: "Garage Door Open 10"
report: "enable"
report: "default"
- platform: template
name: "Garage Door Open 12"
report: "force"