Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(zigbee): Add check, boolean returns, fix Analog, add optional reset on factoryReset #11153

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ void loop() {
// If key pressed for more than 10secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
// Optional set reset in factoryReset to false, to not restart device after erasing nvram, but set it to endless sleep manually instead
Zigbee.factoryReset(false);
Serial.println("Going to endless sleep, press RESET button or power off/on the device to wake up");
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
esp_deep_sleep_start();
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions libraries/Zigbee/src/ZigbeeCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,16 +368,23 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
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();
Zigbee.factoryReset(true);
}
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;
}
}

void ZigbeeCore::factoryReset() {
log_v("Factory resetting Zigbee stack, device will reboot");
esp_zb_factory_reset();
void ZigbeeCore::factoryReset(bool restart) {
if (restart) {
log_v("Factory resetting Zigbee stack, device will reboot");
esp_zb_factory_reset();
}
else {
log_v("Factory resetting Zigbee NVRAM to factory default");
log_w("The device will not reboot, to take effect please reboot the device manually");
esp_zb_zcl_reset_nvram_to_factory_default();
}
}

void ZigbeeCore::scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor) {
Expand Down
2 changes: 1 addition & 1 deletion libraries/Zigbee/src/ZigbeeCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class ZigbeeCore {
zigbee_scan_result_t *getScanResult();
void scanDelete();

void factoryReset();
void factoryReset(bool restart = true);

// Friend function declaration to allow access to private members
friend void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
Expand Down
193 changes: 154 additions & 39 deletions libraries/Zigbee/src/ZigbeeEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,38 +32,38 @@ void ZigbeeEP::setVersion(uint8_t version) {
_ep_config.app_device_version = version;
}

void ZigbeeEP::setManufacturerAndModel(const char *name, const char *model) {
bool ZigbeeEP::setManufacturerAndModel(const char *name, const char *model) {
// Convert manufacturer to ZCL string
size_t length = strlen(name);
if (length > 32) {
log_e("Manufacturer name is too long");
return;
size_t name_length = strlen(name);
size_t model_length = strlen(model);
if (name_length > 32 || model_length > 32) {
log_e("Manufacturer or model name is too long");
return false;
}
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
char *zb_name = new char[length + 2];
char *zb_name = new char[name_length + 2];
char *zb_model = new char[model_length + 2];
// Store the length as the first element
zb_name[0] = static_cast<char>(length); // Cast size_t to char
zb_name[0] = static_cast<char>(name_length); // Cast size_t to char
zb_model[0] = static_cast<char>(model_length);
// Use memcpy to copy the characters to the result array
memcpy(zb_name + 1, name, length);
memcpy(zb_name + 1, name, name_length);
memcpy(zb_model + 1, model, model_length);
// Null-terminate the array
zb_name[length + 1] = '\0';

// Convert model to ZCL string
length = strlen(model);
if (length > 32) {
log_e("Model name is too long");
delete[] zb_name;
return;
}
char *zb_model = new char[length + 2];
zb_model[0] = static_cast<char>(length);
memcpy(zb_model + 1, model, length);
zb_model[length + 1] = '\0';
zb_name[name_length + 1] = '\0';
zb_model[model_length + 1] = '\0';

// Get the basic cluster and update the manufacturer and model attributes
esp_zb_attribute_list_t *basic_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)zb_name);
esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)zb_model);
esp_err_t ret_manufacturer = esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)zb_name);
esp_err_t ret_model = esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)zb_model);
if(ret_manufacturer != ESP_OK || ret_model != ESP_OK) {
log_e("Failed to set manufacturer (0x%x) or model (0x%x)", ret_manufacturer, ret_model);
}

delete[] zb_name;
delete[] zb_model;
return ret_manufacturer == ESP_OK && ret_model == ESP_OK;
}

void ZigbeeEP::setPowerSource(zb_power_source_t power_source, uint8_t battery_percentage) {
Expand All @@ -72,6 +72,9 @@ void ZigbeeEP::setPowerSource(zb_power_source_t power_source, uint8_t battery_pe

if (power_source == ZB_POWER_SOURCE_BATTERY) {
// Add power config cluster and battery percentage attribute
if (battery_percentage > 100) {
battery_percentage = 100;
}
battery_percentage = battery_percentage * 2;
esp_zb_attribute_list_t *power_config_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG);
esp_zb_power_config_cluster_add_attr(power_config_cluster, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID, (void *)&battery_percentage);
Expand All @@ -80,23 +83,29 @@ void ZigbeeEP::setPowerSource(zb_power_source_t power_source, uint8_t battery_pe
_power_source = power_source;
}

void ZigbeeEP::setBatteryPercentage(uint8_t percentage) {
bool ZigbeeEP::setBatteryPercentage(uint8_t percentage) {
// 100% = 200 in decimal, 0% = 0
// Convert percentage to 0-200 range
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
if (percentage > 100) {
percentage = 100;
}
percentage = percentage * 2;
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_set_attribute_val(
ret = esp_zb_zcl_set_attribute_val(
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID, &percentage,
false
);
esp_zb_lock_release();
if(ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set battery percentage: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
log_v("Battery percentage updated");
return true;
}

void ZigbeeEP::reportBatteryPercentage() {
bool ZigbeeEP::reportBatteryPercentage() {
/* Send report attributes command */
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
Expand All @@ -107,9 +116,14 @@ void ZigbeeEP::reportBatteryPercentage() {
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;

esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
esp_zb_lock_release();
if(ret != ESP_OK) {
log_e("Failed to report battery percentage: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
log_v("Battery percentage reported");
return true;
}

char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
Expand All @@ -134,7 +148,9 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_i
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_field = attributes;

// clear read manufacturer
if (_read_manufacturer != nullptr) {
free(_read_manufacturer);
}
_read_manufacturer = nullptr;

esp_zb_lock_acquire(portMAX_DELAY);
Expand Down Expand Up @@ -170,7 +186,9 @@ char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_add
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_field = attributes;

// clear read model
if (_read_model != nullptr) {
free(_read_model);
}
_read_model = nullptr;

esp_zb_lock_acquire(portMAX_DELAY);
Expand Down Expand Up @@ -260,18 +278,31 @@ void ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) {
esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_client, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
}

void ZigbeeEP::setTime(tm time) {
bool ZigbeeEP::setTime(tm time) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
time_t utc_time = mktime(&time);
log_d("Setting time to %lld", utc_time);
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, &utc_time, false);
ret = esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, &utc_time, false);
esp_zb_lock_release();
if(ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set time: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
return true;
}

void ZigbeeEP::setTimezone(int32_t gmt_offset) {
bool ZigbeeEP::setTimezone(int32_t gmt_offset) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
log_d("Setting timezone to %d", gmt_offset);
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, &gmt_offset, false);
ret = esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, &gmt_offset, false);
esp_zb_lock_release();
if(ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set timezone: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
return true;
}

tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
Expand Down Expand Up @@ -390,7 +421,7 @@ void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) {
// uint8_t max_data_size; /*!< The maximum size of OTA data */
// } esp_zb_zcl_ota_upgrade_client_variable_t;

void ZigbeeEP::addOTAClient(
bool ZigbeeEP::addOTAClient(
uint32_t file_version, uint32_t downloaded_file_ver, uint16_t hw_version, uint16_t manufacturer, uint16_t image_type, uint8_t max_data_size
) {

Expand All @@ -410,11 +441,23 @@ void ZigbeeEP::addOTAClient(
uint16_t ota_upgrade_server_addr = 0xffff;
uint8_t ota_upgrade_server_ep = 0xff;

ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_CLIENT_DATA_ID, (void *)&variable_config));
ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ADDR_ID, (void *)&ota_upgrade_server_addr));
ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ENDPOINT_ID, (void *)&ota_upgrade_server_ep));

ESP_ERROR_CHECK(esp_zb_cluster_list_add_ota_cluster(_cluster_list, ota_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE));
esp_err_t ret = esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_CLIENT_DATA_ID, (void *)&variable_config);
if (ret != ESP_OK) {
log_e("Failed to add OTA client data: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ADDR_ID, (void *)&ota_upgrade_server_addr);
if (ret != ESP_OK) {
log_e("Failed to add OTA server address: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ENDPOINT_ID, (void *)&ota_upgrade_server_ep);
if (ret != ESP_OK) {
log_e("Failed to add OTA server endpoint: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
esp_zb_cluster_list_add_ota_cluster(_cluster_list, ota_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
return true;
}

static void findOTAServer(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
Expand Down Expand Up @@ -445,4 +488,76 @@ void ZigbeeEP::requestOTAUpdate() {
esp_zb_lock_release();
}

const char* ZigbeeEP::esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS:
return "Success";
case ESP_ZB_ZCL_STATUS_FAIL:
return "Fail";
case ESP_ZB_ZCL_STATUS_NOT_AUTHORIZED:
return "Not authorized";
case ESP_ZB_ZCL_STATUS_MALFORMED_CMD:
return "Malformed command";
case ESP_ZB_ZCL_STATUS_UNSUP_CLUST_CMD:
return "Unsupported cluster command";
case ESP_ZB_ZCL_STATUS_UNSUP_GEN_CMD:
return "Unsupported general command";
case ESP_ZB_ZCL_STATUS_UNSUP_MANUF_CLUST_CMD:
return "Unsupported manufacturer cluster command";
case ESP_ZB_ZCL_STATUS_UNSUP_MANUF_GEN_CMD:
return "Unsupported manufacturer general command";
case ESP_ZB_ZCL_STATUS_INVALID_FIELD:
return "Invalid field";
case ESP_ZB_ZCL_STATUS_UNSUP_ATTRIB:
return "Unsupported attribute";
case ESP_ZB_ZCL_STATUS_INVALID_VALUE:
return "Invalid value";
case ESP_ZB_ZCL_STATUS_READ_ONLY:
return "Read only";
case ESP_ZB_ZCL_STATUS_INSUFF_SPACE:
return "Insufficient space";
case ESP_ZB_ZCL_STATUS_DUPE_EXISTS:
return "Duplicate exists";
case ESP_ZB_ZCL_STATUS_NOT_FOUND:
return "Not found";
case ESP_ZB_ZCL_STATUS_UNREPORTABLE_ATTRIB:
return "Unreportable attribute";
case ESP_ZB_ZCL_STATUS_INVALID_TYPE:
return "Invalid type";
case ESP_ZB_ZCL_STATUS_WRITE_ONLY:
return "Write only";
case ESP_ZB_ZCL_STATUS_INCONSISTENT:
return "Inconsistent";
case ESP_ZB_ZCL_STATUS_ACTION_DENIED:
return "Action denied";
case ESP_ZB_ZCL_STATUS_TIMEOUT:
return "Timeout";
case ESP_ZB_ZCL_STATUS_ABORT:
return "Abort";
case ESP_ZB_ZCL_STATUS_INVALID_IMAGE:
return "Invalid OTA upgrade image";
case ESP_ZB_ZCL_STATUS_WAIT_FOR_DATA:
return "Server does not have data block available yet";
case ESP_ZB_ZCL_STATUS_NO_IMAGE_AVAILABLE:
return "No image available";
case ESP_ZB_ZCL_STATUS_REQUIRE_MORE_IMAGE:
return "Require more image";
case ESP_ZB_ZCL_STATUS_NOTIFICATION_PENDING:
return "Notification pending";
case ESP_ZB_ZCL_STATUS_HW_FAIL:
return "Hardware failure";
case ESP_ZB_ZCL_STATUS_SW_FAIL:
return "Software failure";
case ESP_ZB_ZCL_STATUS_CALIB_ERR:
return "Calibration error";
case ESP_ZB_ZCL_STATUS_UNSUP_CLUST:
return "Cluster is not found on the target endpoint";
case ESP_ZB_ZCL_STATUS_LIMIT_REACHED:
return "Limit reached";
default:
return "Unknown status";
}
}


#endif // CONFIG_ZB_ENABLED
Loading
Loading