ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
zigbee_esp32.h
Go to the documentation of this file.
1#pragma once
2
4#ifdef USE_ESP32
5#ifdef USE_ZIGBEE
6
7#include <map>
8#include <tuple>
9#include <atomic>
10
11#include "esp_zigbee.h"
12#include "ezbee/zha.h"
18#include "zigbee_event_esp32.h"
19
20namespace esphome::zigbee {
21
22/* Zigbee configuration */
23static const uint8_t MAX_CHILDREN = 10;
24static const uint32_t EZB_PRIMARY_CHANNEL_MASK = 0x07FFF800U; /* channels 11-26 */
25static constexpr uint8_t MAX_ZB_QUEUE_SIZE = 32;
26
27#define EZB_DEFAULT_RADIO_CONFIG() \
28 { .radio_mode = ESP_ZIGBEE_RADIO_MODE_NATIVE, }
29
30uint8_t *get_zcl_string(const char *str, uint8_t max_size, bool use_max_size = false);
31
32struct AttrValue {
33 ezb_zcl_attr_desc_t attr_desc;
34 uint8_t value[4];
35};
36
37class ZigbeeAttribute;
38
39class ZigbeeComponent final : public Component {
40 public:
41 void setup() override;
42 void loop() override;
43 void dump_config() override;
44
45 void set_sleepy(bool sleepy) { this->sleepy_ = sleepy; }
46 void set_keep_alive(uint32_t keep_alive) { this->keep_alive_ = keep_alive; }
47 void set_basic_cluster(const char *model, const char *manufacturer, const char *location, const char *sw_build_id,
48 uint8_t power_source, uint8_t app_version);
49 void add_cluster(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role);
50 void create_default_cluster(uint8_t endpoint_id, uint16_t device_id);
51 bool register_device();
52
53 template<typename T>
54 void add_attr(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,
55 uint8_t max_size, T value);
56
57 template<typename T>
58 void add_attr(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id, uint8_t max_size, T value);
59
60 static bool app_signal_handler(const ezb_app_signal_t *app_signal);
61 static void esp_zigbee_alarm_bdb_commissioning(ezb_bdb_comm_mode_mask_t mode);
62
63 void factory_reset();
64 void report();
65
66 template<typename F> void add_on_join_callback(F &&cb) { this->join_cb_.add(std::forward<F>(cb)); }
67 template<typename F> void add_on_start_callback(F &&cb) { this->start_cb_.add(std::forward<F>(cb)); }
68
69 bool is_battery_powered() { return this->basic_cluster_data_.power_source == EZB_ZCL_BASIC_POWER_SOURCE_BATTERY; }
70
71 // True after the Zigbee stack has been initialized and the device has started up. Is set before the stack started
72 // network commissioning or has joined a network and won't be reset until the device is rebooted.
73 bool is_started() { return this->started_; }
74
75 // True if the device has joined a network and is ready to send and receive messages.
76 bool is_joined() { return this->joined_; }
77
78 protected:
79 bool sleepy_{false};
81 struct {
82 uint8_t *model;
83 uint8_t *manufacturer;
84 uint8_t *date;
85 uint8_t *location;
86 uint8_t *sw_build_id;
87 uint8_t power_source;
88 uint8_t app_version;
90#ifdef CONFIG_ZB_ZED
91 ezb_nwk_device_type_t device_role_ = EZB_NWK_DEVICE_TYPE_END_DEVICE;
92#else
93 ezb_nwk_device_type_t device_role_ = EZB_NWK_DEVICE_TYPE_ROUTER;
94#endif
95 void update_basic_cluster_(ezb_af_ep_desc_t ep_desc);
96 template<typename T>
97 void add_attr_(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,
98 T *value_p);
99 void handle_attribute_(ezb_zcl_message_info_t info, ezb_zcl_attribute_t attribute);
100 // attributes_ will be used during operation in zigbee callbacks to update the attribute values and trigger
101 // automations
102 // key tuple could be replaced by single 64 (48) bit int with bit fields for endpoint, cluster, role and attr_id
103 std::map<std::tuple<uint8_t, uint16_t, uint8_t, uint16_t>, ZigbeeAttribute *> attributes_;
104 std::vector<AttrValue> attr_values_;
105 ezb_af_device_desc_t dev_desc_ = ezb_af_create_device_desc();
107 template<typename... Args> friend void enqueue_zb_event(Args... args);
109 // Pool sized to queue capacity (SIZE-1) because LockFreeQueue<T,N> is a ring
110 // buffer that holds N-1 elements (one slot distinguishes full from empty).
111 // This guarantees allocate() returns nullptr before push() can fail, which:
112 // 1. Prevents leaking a pool slot (the Nth allocate succeeds but push fails)
113 // 2. Avoids needing release() on the producer path after a failed push(),
114 // preserving the SPSC contract on the pool's internal free list
117 bool start_reported_{false};
118 std::atomic<bool> started_ = false;
119 std::atomic<bool> joined_ = false;
120 std::atomic<bool> join_pending_ = false;
121 std::atomic<bool> factory_new_ = false;
122 // TODO: remove when esp-zigbee-lib fixes set_value before init
123 bool string_attr_exists_(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id);
124};
125
126template<typename T>
127void ZigbeeComponent::add_attr(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,
128 uint8_t max_size, T value) {
129 this->add_attr<T>(nullptr, endpoint_id, cluster_id, role, attr_id, max_size, value);
130}
131
132template<typename T>
133void ZigbeeComponent::add_attr(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role,
134 uint16_t attr_id, uint8_t max_size, T value) {
135 // The size byte of the zcl_str must be set to the maximum value,
136 // even though the initial string may be shorter.
137 if constexpr (std::is_same<T, std::string>::value) {
138 if (this->string_attr_exists_(endpoint_id, cluster_id, role, attr_id)) {
139 if (attr != nullptr) {
140 this->attributes_[{endpoint_id, cluster_id, role, attr_id}] = attr;
141 }
142 return;
143 }
144 auto zcl_str = get_zcl_string(value.c_str(), max_size, true);
145 add_attr_(attr, endpoint_id, cluster_id, role, attr_id, zcl_str);
146 delete[] zcl_str;
147 } else if constexpr (std::is_convertible<T, const char *>::value) {
148 if (this->string_attr_exists_(endpoint_id, cluster_id, role, attr_id)) {
149 if (attr != nullptr) {
150 this->attributes_[{endpoint_id, cluster_id, role, attr_id}] = attr;
151 }
152 return;
153 }
154 auto zcl_str = get_zcl_string(value, max_size, true);
155 add_attr_(attr, endpoint_id, cluster_id, role, attr_id, zcl_str);
156 delete[] zcl_str;
157 } else {
158 add_attr_(attr, endpoint_id, cluster_id, role, attr_id, &value);
159 }
160}
161
162template<typename T>
163void ZigbeeComponent::add_attr_(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role,
164 uint16_t attr_id, T *value_p) {
165 ezb_af_ep_desc_t ep_desc = ezb_af_device_get_endpoint_desc(this->dev_desc_, endpoint_id);
166 if (ep_desc == NULL) {
167 return;
168 }
169 ezb_zcl_cluster_desc_t cluster_desc = ezb_af_endpoint_get_cluster_desc(ep_desc, cluster_id, role);
170 if (cluster_desc == NULL) {
171 return;
172 }
173 // TODO: revert when esp-zigbee-lib fixes set_value before init
174 ezb_zcl_attr_desc_t attr_desc = ezb_zcl_cluster_get_attr_desc(cluster_desc, attr_id, EZB_ZCL_STD_MANUF_CODE);
175
176 if (attr_desc != NULL) {
177 static_assert(sizeof(*value_p) <= 4);
178 AttrValue attr_value;
179 attr_value.attr_desc = attr_desc;
180 memcpy(&attr_value.value, value_p, sizeof(*value_p));
181 attr_values_.push_back(attr_value);
182 } else {
183 esphome_zb_cluster_add_attr(cluster_id, cluster_desc, attr_id, value_p);
184 }
185
186 if (attr != nullptr) {
187 this->attributes_[{endpoint_id, cluster_id, role, attr_id}] = attr;
188 }
189}
190
191} // namespace esphome::zigbee
192
193#endif
194#endif
BedjetMode mode
BedJet operating mode.
std::vector< AttrValue > attr_values_
CallbackManager< void(bool)> join_cb_
struct esphome::zigbee::ZigbeeComponent::@206 basic_cluster_data_
bool string_attr_exists_(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id)
void create_default_cluster(uint8_t endpoint_id, uint16_t device_id)
friend void enqueue_zb_event(Args... args)
std::atomic< bool > factory_new_
void handle_attribute_(ezb_zcl_message_info_t info, ezb_zcl_attribute_t attribute)
esphome::LockFreeQueue< ZBEvent, MAX_ZB_QUEUE_SIZE > zb_events_
std::map< std::tuple< uint8_t, uint16_t, uint8_t, uint16_t >, ZigbeeAttribute * > attributes_
void update_basic_cluster_(ezb_af_ep_desc_t ep_desc)
void add_cluster(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role)
static void esp_zigbee_alarm_bdb_commissioning(ezb_bdb_comm_mode_mask_t mode)
LazyCallbackManager< void()> start_cb_
static bool app_signal_handler(const ezb_app_signal_t *app_signal)
void set_keep_alive(uint32_t keep_alive)
std::atomic< bool > join_pending_
ezb_nwk_device_type_t device_role_
void set_basic_cluster(const char *model, const char *manufacturer, const char *location, const char *sw_build_id, uint8_t power_source, uint8_t app_version)
esphome::EventPool< ZBEvent, MAX_ZB_QUEUE_SIZE - 1 > zb_event_pool_
void add_attr(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id, uint8_t max_size, T value)
ezb_af_device_desc_t dev_desc_
void add_attr_(ZigbeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id, T *value_p)
uint8_t * get_zcl_string(const char *str, uint8_t max_size, bool use_max_size)
static void uint32_t
ezb_zcl_attr_desc_t attr_desc
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)