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.
This commit is contained in:
Chirag Atal
2022-04-08 14:56:25 +05:30
parent 0685068c26
commit 62a4fbbd3d
5 changed files with 120 additions and 12 deletions
+3
View File
@@ -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 */
@@ -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;
@@ -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
+38 -5
View File
@@ -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");
+38 -1
View File
@@ -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 */