From 62a4fbbd3d7b781ce69ee797f8558efbab3929ad Mon Sep 17 00:00:00 2001 From: Chirag Atal Date: Fri, 8 Apr 2022 14:56:25 +0530 Subject: [PATCH] attribute: Add support for overriding attribute read and write This can also be used for an attribute whose value is dynamic and needs to be fetched from the application. --- components/esp_matter/esp_matter.h | 3 ++ .../esp_matter/esp_matter_attribute_utils.cpp | 43 ++++++++++++++++--- .../esp_matter/esp_matter_attribute_utils.h | 4 ++ components/esp_matter/esp_matter_core.cpp | 43 ++++++++++++++++--- components/esp_matter/esp_matter_core.h | 39 ++++++++++++++++- 5 files changed, 120 insertions(+), 12 deletions(-) diff --git a/components/esp_matter/esp_matter.h b/components/esp_matter/esp_matter.h index e20180f2b..9aa67fbfd 100644 --- a/components/esp_matter/esp_matter.h +++ b/components/esp_matter/esp_matter.h @@ -85,6 +85,9 @@ typedef enum attribute_flags { ATTRIBUTE_FLAG_SINGLETON = ATTRIBUTE_MASK_SINGLETON, /* 0x20 */ ATTRIBUTE_FLAG_CLIENT = ATTRIBUTE_MASK_CLIENT, /* 0x40 */ ATTRIBUTE_FLAG_NULLABLE = ATTRIBUTE_MASK_NULLABLE, /* 0x80 */ + /** The attribute read and write are overridden. The attribute value will be fetched from and will be updated using + the override callback. The value of this attribute is not maintained internally. */ + ATTRIBUTE_FLAG_OVERRIDE = ATTRIBUTE_FLAG_NULLABLE << 1, /* 0x100 */ } attribute_flags_t; /** Command flags */ diff --git a/components/esp_matter/esp_matter_attribute_utils.cpp b/components/esp_matter/esp_matter_attribute_utils.cpp index 27c724fa9..c713fe831 100644 --- a/components/esp_matter/esp_matter_attribute_utils.cpp +++ b/components/esp_matter/esp_matter_attribute_utils.cpp @@ -405,11 +405,24 @@ esp_err_t set_callback(callback_t callback, void *priv_data) return ESP_OK; } -esp_err_t send_callback(callback_type_t type, int endpoint_id, int cluster_id, int attribute_id, - esp_matter_attr_val_t *val) +static esp_err_t execute_callback(callback_type_t type, int endpoint_id, int cluster_id, int attribute_id, + esp_matter_attr_val_t *val) { if (attribute_callback) { - attribute_callback(type, endpoint_id, cluster_id, attribute_id, val, attribute_callback_priv_data); + return attribute_callback(type, endpoint_id, cluster_id, attribute_id, val, attribute_callback_priv_data); + } + return ESP_OK; +} + +static esp_err_t execute_override_callback(attribute_t *attribute, callback_type_t type, int endpoint_id, + int cluster_id, int attribute_id, esp_matter_attr_val_t *val) +{ + callback_t override_callback = attribute::get_override_callback(attribute); + if (override_callback) { + return override_callback(type, endpoint_id, cluster_id, attribute_id, val, NULL); + } else { + ESP_LOGI(TAG, "Attribute override callback not set, calling the common callback"); + return execute_callback(type, endpoint_id, cluster_id, attribute_id, val); } return ESP_OK; } @@ -922,7 +935,7 @@ Status MatterPreAttributeChangeCallback(const chip::app::ConcreteAttributePath & attribute::val_print(endpoint_id, cluster_id, attribute_id, &val); /* Callback to application */ - esp_err_t err = send_callback(attribute::PRE_UPDATE, endpoint_id, cluster_id, attribute_id, &val); + esp_err_t err = execute_callback(attribute::PRE_UPDATE, endpoint_id, cluster_id, attribute_id, &val); if (err != ESP_OK) { return Status::Failure; } @@ -939,7 +952,7 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath &p attribute::get_attr_val_from_data(&val, type, size, value); /* Callback to application */ - send_callback(attribute::POST_UPDATE, endpoint_id, cluster_id, attribute_id, &val); + execute_callback(attribute::POST_UPDATE, endpoint_id, cluster_id, attribute_id, &val); } EmberAfStatus emberAfExternalAttributeReadCallback(EndpointId endpoint_id, ClusterId cluster_id, @@ -956,7 +969,17 @@ EmberAfStatus emberAfExternalAttributeReadCallback(EndpointId endpoint_id, Clust cluster_t *cluster = cluster::get(endpoint, cluster_id); attribute_t *attribute = attribute::get(cluster, attribute_id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); - attribute::get_val(attribute, &val); + + int flags = attribute::get_flags(attribute); + if (flags & ATTRIBUTE_FLAG_OVERRIDE) { + esp_err_t err = execute_override_callback(attribute, attribute::READ, endpoint_id, cluster_id, attribute_id, + &val); + if (err != ESP_OK) { + return EMBER_ZCL_STATUS_FAILURE; + } + } else { + attribute::get_val(attribute, &val); + } /* Print */ attribute::val_print(endpoint_id, cluster_id, attribute_id, &val); @@ -994,6 +1017,14 @@ EmberAfStatus emberAfExternalAttributeWriteCallback(EndpointId endpoint_id, Clus esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_attr_val_from_data(&val, matter_attribute->attributeType, matter_attribute->size, buffer); + int flags = attribute::get_flags(attribute); + if (flags & ATTRIBUTE_FLAG_OVERRIDE) { + esp_err_t err = execute_override_callback(attribute, attribute::WRITE, endpoint_id, cluster_id, attribute_id, + &val); + EmberAfStatus status = (err == ESP_OK) ? EMBER_ZCL_STATUS_SUCCESS : EMBER_ZCL_STATUS_FAILURE; + return status; + } + /* Update val */ attribute::set_val(attribute, &val); return EMBER_ZCL_STATUS_SUCCESS; diff --git a/components/esp_matter/esp_matter_attribute_utils.h b/components/esp_matter/esp_matter_attribute_utils.h index a76e4401f..01f2f1166 100644 --- a/components/esp_matter/esp_matter_attribute_utils.h +++ b/components/esp_matter/esp_matter_attribute_utils.h @@ -176,6 +176,10 @@ typedef enum callback_type { PRE_UPDATE, /** Callback after updating the value in the database */ POST_UPDATE, + /** Callback for reading the attribute value. This is used when the `ATTRIBUTE_FLAG_OVERRIDE` is set. */ + READ, + /** Callback for writing the attribute value. This is used when the `ATTRIBUTE_FLAG_OVERRIDE` is set. */ + WRITE, } callback_type_t; /** Callback for attribute update diff --git a/components/esp_matter/esp_matter_core.cpp b/components/esp_matter/esp_matter_core.cpp index dcd882c20..2c048b034 100644 --- a/components/esp_matter/esp_matter_core.cpp +++ b/components/esp_matter/esp_matter_core.cpp @@ -57,24 +57,25 @@ static const char *TAG = "esp_matter_core"; namespace esp_matter { typedef struct _attribute { int attribute_id; - uint8_t flags; + uint16_t flags; esp_matter_attr_val_t val; esp_matter_attr_bounds_t *bounds; EmberAfDefaultOrMinMaxAttributeValue default_value; uint16_t default_value_size; + attribute::callback_t override_callback; struct _attribute *next; } _attribute_t; typedef struct _command { int command_id; - uint8_t flags; + uint16_t flags; command::callback_t callback; struct _command *next; } _command_t; typedef struct _cluster { int cluster_id; - uint8_t flags; + uint16_t flags; const cluster::function_generic_t *function_list; cluster::plugin_server_init_callback_t plugin_server_init_callback; cluster::plugin_client_init_callback_t plugin_client_init_callback; @@ -86,7 +87,7 @@ typedef struct _cluster { typedef struct _endpoint { int endpoint_id; int device_type_id; - uint8_t flags; + uint16_t flags; _cluster_t *cluster_list; EmberAfEndpointType *endpoint_type; DataVersion *data_versions_ptr; @@ -880,6 +881,38 @@ esp_matter_attr_bounds_t *get_bounds(attribute_t *attribute) return current_attribute->bounds; } +uint16_t get_flags(attribute_t *attribute) +{ + if (!attribute) { + ESP_LOGE(TAG, "Attribute cannot be NULL"); + return 0; + } + _attribute_t *current_attribute = (_attribute_t *)attribute; + return current_attribute->flags; +} + +esp_err_t set_override_callback(attribute_t *attribute, callback_t callback) +{ + if (!attribute) { + ESP_LOGE(TAG, "Attribute cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + _attribute_t *current_attribute = (_attribute_t *)attribute; + current_attribute->override_callback = callback; + current_attribute->flags |= ATTRIBUTE_FLAG_OVERRIDE; + return ESP_OK; +} + +callback_t get_override_callback(attribute_t *attribute) +{ + if (!attribute) { + ESP_LOGE(TAG, "Attribute cannot be NULL"); + return NULL; + } + _attribute_t *current_attribute = (_attribute_t *)attribute; + return current_attribute->override_callback; +} + } /* attribute */ namespace command { @@ -990,7 +1023,7 @@ callback_t get_callback(command_t *command) return current_command->callback; } -int get_flags(command_t *command) +uint16_t get_flags(command_t *command) { if (!command) { ESP_LOGE(TAG, "Command cannot be NULL"); diff --git a/components/esp_matter/esp_matter_core.h b/components/esp_matter/esp_matter_core.h index 27b72da01..ef9a49d4f 100644 --- a/components/esp_matter/esp_matter_core.h +++ b/components/esp_matter/esp_matter_core.h @@ -498,6 +498,43 @@ esp_err_t add_bounds(attribute_t *attribute, esp_matter_attr_val_t min, esp_matt */ esp_matter_attr_bounds_t *get_bounds(attribute_t *attribute); +/** Get attribute flags + * + * Get the attribute flags for the attribute. + * + * @param[in] attribute Attribute handle. + * + * @return Attribute flags. + */ +uint16_t get_flags(attribute_t *attribute); + +/** Set attribute override + * + * Set the override callback for the attribute. For attribute read and write calls, instead of doing that from the + * common database, this callback will be called. + * + * This can be used if the application or some component wants to maintain the attribute's value in the application or + * in that component respectively. It can also be used if the attribute value needs to be dynamically fetched and is + * difficult to maintain in the database. + * + * @param[in] attribute Attribute handle. + * @param[in] callback Override callback. + * + * @return ESP_OK on success. + * @return error in case of failure. + */ +esp_err_t set_override_callback(attribute_t *attribute, callback_t callback); + +/** Get attribute override + * + * Get the override callback for the attribute. + * + * @param[in] attribute Attribute handle. + * + * @return Attribute override callback. + */ +callback_t get_override_callback(attribute_t *attribute); + } /* attribute */ namespace command { @@ -599,7 +636,7 @@ callback_t get_callback(command_t *command); * * @return Command flags. */ -int get_flags(command_t *command); +uint16_t get_flags(command_t *command); } /* command */