diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index e67b0ec8578..67cb47ee319 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -76,6 +76,7 @@ scanComplete KEYWORD2 getScanResult KEYWORD2 scanDelete KEYWORD2 factoryReset KEYWORD2 +allowMultiEndpointBinding KEYWORD2 # Common ZigbeeEP setEpConfig KEYWORD2 diff --git a/libraries/Zigbee/src/ZigbeeCore.cpp b/libraries/Zigbee/src/ZigbeeCore.cpp index 2e48a9d2461..9706d732752 100644 --- a/libraries/Zigbee/src/ZigbeeCore.cpp +++ b/libraries/Zigbee/src/ZigbeeCore.cpp @@ -46,6 +46,7 @@ ZigbeeCore::ZigbeeCore() { _scan_duration = 3; // default scan duration _rx_on_when_idle = true; _debug = false; + _allow_multi_endpoint_binding = false; _global_default_response_cb = nullptr; // Initialize global callback to nullptr if (!lock) { lock = xSemaphoreCreateBinary(); @@ -392,7 +393,9 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); (*it)->findEndpoint(&cmd_req); log_d("Endpoint %d is searching for device", (*it)->getEndpoint()); - break; // Only one endpoint per device + if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility + break; + } } } } @@ -422,11 +425,13 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { break; } } - log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); if (!found) { + log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); (*it)->findEndpoint(&cmd_req); log_d("Endpoint %d is searching for device", (*it)->getEndpoint()); - break; // Only one endpoint per device + if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility + break; + } } } } diff --git a/libraries/Zigbee/src/ZigbeeCore.h b/libraries/Zigbee/src/ZigbeeCore.h index ae92e21f3a4..6295832fa03 100644 --- a/libraries/Zigbee/src/ZigbeeCore.h +++ b/libraries/Zigbee/src/ZigbeeCore.h @@ -117,6 +117,7 @@ class ZigbeeCore { zigbee_scan_result_t *_scan_result; SemaphoreHandle_t lock; bool _debug; + bool _allow_multi_endpoint_binding; // Global default response callback void (*_global_default_response_cb)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster); @@ -196,6 +197,13 @@ class ZigbeeCore { return _debug; } + void allowMultiEndpointBinding(bool allow) { + _allow_multi_endpoint_binding = allow; + } + bool allowMultiEndpointBinding() { + return _allow_multi_endpoint_binding; + } + // Set global default response callback void onGlobalDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster)) { _global_default_response_cb = callback; diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index ce1d2befa26..a873205808f 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -149,6 +149,8 @@ class ZigbeeEP { // list of all handlers function calls, to be override by EPs implementation virtual void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {}; virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; + virtual void + zbWriteAttributeResponse(uint16_t cluster_id, uint16_t attribute_id, esp_zb_zcl_status_t status, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {}; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index e54790cc392..29c4e670f66 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -42,6 +42,7 @@ static bool s_tagid_received = false; static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message); static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message); static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message); +static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message); static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message); static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message); static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message); @@ -72,8 +73,9 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_OTA_UPGRADE_QUERY_IMAGE_RESP_CB_ID: ret = zb_ota_upgrade_query_image_resp_handler((esp_zb_zcl_ota_upgrade_query_image_resp_message_t *)message); break; - case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; - default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; + case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; + case ESP_ZB_CORE_CMD_WRITE_ATTR_RESP_CB_ID: ret = zb_cmd_write_attr_resp_handler((esp_zb_zcl_cmd_write_attr_resp_message_t *)message); break; + default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } return ret; } @@ -170,6 +172,36 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re return ESP_OK; } +static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message) { + if (!message) { + log_e("Empty message"); + return ESP_FAIL; + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; + } + log_v( + "Write attribute response: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr, + message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster + ); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + esp_zb_zcl_write_attr_resp_variable_t *variable = message->variables; + while (variable) { + log_v("Write attribute response: status(%d), cluster(0x%x), attribute(0x%x)", variable->status, message->info.cluster, variable->attribute_id); + if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) { + (*it)->zbWriteAttributeResponse( + message->info.cluster, variable->attribute_id, variable->status, message->info.src_endpoint, message->info.src_address + ); + } + variable = variable->next; + } + } + } + return ESP_OK; +} + static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message) { if (!message) { log_e("Empty message");