Skip to content

Commit

Permalink
feat(Zigbee): Recall bounded devices after reboot + IEEE address opti…
Browse files Browse the repository at this point in the history
…on for commands (#10676)

* feat(zigbee): Recall bound devices after reboot

* fix(zigbee): Add missing locks + allow printBoundDevices to Serial

* fix(Zigbee): Add locks to temp sensor setReporting

* fix(Zigbee): remove unnecessary space in formatting

* fix(Zigbee): proper parameter in printBoundDevices

* feat(Zigbee): factory reset when removed from network

* fix(zigbee): Update comment

* fix(zigbee): fix serial and add missing factoryReset to example

* ci(pre-commit): Apply automatic fixes

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
  • Loading branch information
P-R-O-C-H-Y and pre-commit-ci-lite[bot] authored Dec 9, 2024
1 parent e159bf6 commit 92dd841
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,6 @@ void loop() {
static uint32_t last_print = 0;
if (millis() - last_print > 30000) {
last_print = millis();
zbSwitch.printBoundDevices();
zbSwitch.printBoundDevices(Serial);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,16 @@ void setup() {
delay(500);
}

// Optional: read manufacturer and model name from the bound light
// Optional: List all bound devices and read manufacturer and model name
std::list<zb_device_params_t *> boundLights = zbSwitch.getBoundDevices();
//List all bound lights
for (const auto &device : boundLights) {
Serial.printf("Device on endpoint %d, short address: 0x%x\n", device->endpoint, device->short_addr);
Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr);
Serial.printf(
"IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", device->ieee_addr[0], device->ieee_addr[1], device->ieee_addr[2], device->ieee_addr[3],
device->ieee_addr[4], device->ieee_addr[5], device->ieee_addr[6], device->ieee_addr[7]
"IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4],
device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
);
Serial.printf("Light manufacturer: %s", zbSwitch.readManufacturer(device->endpoint, device->short_addr));
Serial.printf("Light model: %s", zbSwitch.readModel(device->endpoint, device->short_addr));
Serial.printf("Light manufacturer: %s\r\n", zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr));
Serial.printf("Light model: %s\r\n", zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr));
}

Serial.println();
Expand Down Expand Up @@ -191,6 +190,6 @@ void loop() {
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10000) {
lastPrint = millis();
zbSwitch.printBoundDevices();
zbSwitch.printBoundDevices(Serial);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ void loop() {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
Expand Down
77 changes: 76 additions & 1 deletion libraries/Zigbee/src/ZigbeeCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
} else {
Zigbee._connected = true;
}
Zigbee.searchBindings();
}
} else {
/* commissioning failed */
Expand Down Expand Up @@ -309,7 +310,6 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
Bit 6 – Security capability
Bit 7 – Reserved
*/

// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
Expand All @@ -329,6 +329,12 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
}
}
break;
case ESP_ZB_ZDO_SIGNAL_LEAVE: // End Device + Router
// Device was removed from the network, factory reset the device
if ((zigbee_role_t)Zigbee.getRole() != ZIGBEE_COORDINATOR) {
Zigbee.factoryReset();
}
break;
default: log_v("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status)); break;
}
}
Expand Down Expand Up @@ -391,6 +397,75 @@ void ZigbeeCore::scanDelete() {
_scan_status = ZB_SCAN_FAILED;
}

// Recall bounded devices from the binding table after reboot
void ZigbeeCore::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx) {
bool done = true;
esp_zb_zdo_mgmt_bind_param_t *req = (esp_zb_zdo_mgmt_bind_param_t *)user_ctx;
esp_zb_zdp_status_t zdo_status = (esp_zb_zdp_status_t)table_info->status;
log_d("Binding table callback for address 0x%04x with status %d", req->dst_addr, zdo_status);
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
// Print binding table log simple
log_d("Binding table info: total %d, index %d, count %d", table_info->total, table_info->index, table_info->count);

if (table_info->total == 0) {
log_d("No binding table entries found");
free(req);
return;
}

esp_zb_zdo_binding_table_record_t *record = table_info->record;
for (int i = 0; i < table_info->count; i++) {
log_d(
"Binding table record: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", record->src_endp, record->dst_endp, record->cluster_id,
record->dst_addr_mode
);

zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
device->endpoint = record->dst_endp;
if (record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT || record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT) {
device->short_addr = record->dst_address.addr_short;
} else { //ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT
memcpy(device->ieee_addr, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
}

// Add to list of bound devices of proper endpoint
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if ((*it)->getEndpoint() == record->src_endp) {
(*it)->addBoundDevice(device);
log_d(
"Device bound to EP %d -> device endpoint: %d, short addr: 0x%04x, ieee addr: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", record->src_endp,
device->endpoint, device->short_addr, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3],
device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
);
}
}
record = record->next;
}

// Continue reading the binding table
if (table_info->index + table_info->count < table_info->total) {
/* There are unreported binding table entries, request for them. */
req->start_index = table_info->index + table_info->count;
esp_zb_zdo_binding_table_req(req, bindingTableCb, req);
done = false;
}
}

if (done) {
// Print bound devices
log_d("Filling bounded devices finished");
free(req);
}
}

void ZigbeeCore::searchBindings() {
esp_zb_zdo_mgmt_bind_param_t *mb_req = (esp_zb_zdo_mgmt_bind_param_t *)malloc(sizeof(esp_zb_zdo_mgmt_bind_param_t));
mb_req->dst_addr = esp_zb_get_short_address();
mb_req->start_index = 0;
log_d("Requesting binding table for address 0x%04x", mb_req->dst_addr);
esp_zb_zdo_binding_table_req(mb_req, bindingTableCb, (void *)mb_req);
}

// Function to convert enum value to string
const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
switch (deviceId) {
Expand Down
2 changes: 2 additions & 0 deletions libraries/Zigbee/src/ZigbeeCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class ZigbeeCore {
bool zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs);
static void scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor);
const char *getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId);
void searchBindings();
static void bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx);

public:
ZigbeeCore();
Expand Down
56 changes: 45 additions & 11 deletions libraries/Zigbee/src/ZigbeeEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "esp_zigbee_cluster.h"
#include "zcl/esp_zigbee_zcl_power_config.h"

#define ZB_CMD_TIMEOUT 10000 // 10 seconds

bool ZigbeeEP::_is_bound = false;
bool ZigbeeEP::_allow_multiple_binding = false;

Expand Down Expand Up @@ -112,13 +114,20 @@ void ZigbeeEP::reportBatteryPercentage() {
log_v("Battery percentage reported");
}

char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr) {
char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer Manufacture Name & Model Identifier */
esp_zb_zcl_read_attr_cmd_t read_req;
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;

if (short_addr != 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}

read_req.zcl_basic_cmd.src_endpoint = _endpoint;
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;

uint16_t attributes[] = {
Expand All @@ -130,22 +139,31 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr) {
// clear read manufacturer
_read_manufacturer = nullptr;

esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();

//Wait for response or timeout
if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) {
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading manufacturer");
}
return _read_manufacturer;
}

char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr) {
char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer Manufacture Name & Model Identifier */
esp_zb_zcl_read_attr_cmd_t read_req;
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;

if (short_addr != 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}

read_req.zcl_basic_cmd.src_endpoint = _endpoint;
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;

uint16_t attributes[] = {
Expand All @@ -157,11 +175,12 @@ char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr) {
// clear read model
_read_model = nullptr;

esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();

//Wait for response or timeout
//Semaphore take
if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) {
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading model");
}
return _read_model;
Expand All @@ -171,8 +190,23 @@ void ZigbeeEP::printBoundDevices() {
log_i("Bound devices:");
for ([[maybe_unused]]
const auto &device : _bound_devices) {
log_i("Device on endpoint %d, short address: 0x%x", device->endpoint, device->short_addr);
print_ieee_addr(device->ieee_addr);
log_i(
"Device on endpoint %d, short address: 0x%x, ieee address: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", device->endpoint, device->short_addr,
device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1],
device->ieee_addr[0]
);
}
}

void ZigbeeEP::printBoundDevices(Print &print) {
print.println("Bound devices:");
for ([[maybe_unused]]
const auto &device : _bound_devices) {
print.printf(
"Device on endpoint %d, short address: 0x%x, ieee address: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\r\n", device->endpoint, device->short_addr,
device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1],
device->ieee_addr[0]
);
}
}

Expand Down
14 changes: 9 additions & 5 deletions libraries/Zigbee/src/ZigbeeEP.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

/* Useful defines */
#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
#define print_ieee_addr(addr) \
log_i("IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7])
#define XYZ_TO_RGB(X, Y, Z, r, g, b) \
{ \
r = (float)(3.240479 * (X) - 1.537150 * (Y) - 0.498535 * (Z)); \
Expand Down Expand Up @@ -69,6 +67,8 @@ class ZigbeeEP {
}

void printBoundDevices();
void printBoundDevices(Print &print);

std::list<zb_device_params_t *> getBoundDevices() const {
return _bound_devices;
}
Expand All @@ -87,8 +87,8 @@ class ZigbeeEP {
void reportBatteryPercentage();

// Methods to read manufacturer and model name from selected endpoint and short address
char *readManufacturer(uint8_t endpoint, uint16_t short_addr);
char *readModel(uint8_t endpoint, uint16_t short_addr);
char *readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
char *readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);

bool epAllowMultipleBinding() {
return _allow_multiple_binding;
Expand All @@ -108,7 +108,6 @@ class ZigbeeEP {
}

private:
static bool _allow_multiple_binding;
char *_read_manufacturer;
char *_read_model;
void (*_on_identify)(uint16_t time);
Expand All @@ -119,10 +118,15 @@ class ZigbeeEP {
esp_zb_endpoint_config_t _ep_config;
esp_zb_cluster_list_t *_cluster_list;
static bool _is_bound;
static bool _allow_multiple_binding;
std::list<zb_device_params_t *> _bound_devices;
SemaphoreHandle_t lock;
zb_power_source_t _power_source;

void addBoundDevice(zb_device_params_t *device) {
_bound_devices.push_back(device);
_is_bound = true;
}
friend class ZigbeeCore;
};

Expand Down
Loading

0 comments on commit 92dd841

Please sign in to comment.