mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 11:03:05 +00:00
Merge branch 'backport/v1.5/cluster-pre-attr-change-cb' into 'release/v1.5'
[v1.5]: esp_matter/data_model_provider: Add support to call ClusterPreAttributeChangeCallback from esp-matter data model provider. See merge request app-frameworks/esp-matter!1476
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
[codespell]
|
||||
ignore-regex = _
|
||||
ignore-words-list = ot, bootup, requestor, pase, lits
|
||||
ignore-words-list = ot, bootup, requestor, pase, lits, kNo
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_matter_attr_data_buffer.h>
|
||||
#include <esp_matter_attribute_utils.h>
|
||||
#include <esp_matter_cluster.h>
|
||||
#include <esp_matter_data_model.h>
|
||||
#include <esp_matter_data_model_priv.h>
|
||||
#include <esp_matter_data_model_provider.h>
|
||||
#include <esp_matter_attr_data_buffer.h>
|
||||
|
||||
#include <access/Privilege.h>
|
||||
#include <app-common/zap-generated/cluster-objects.h>
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <app/CommandHandlerInterface.h>
|
||||
#include <app/CommandHandlerInterfaceRegistry.h>
|
||||
#include <app/data-model-provider/MetadataTypes.h>
|
||||
#include <app/util/attribute-storage.h>
|
||||
#include <app/util/attribute-table.h>
|
||||
#include <clusters/shared/GlobalIds.h>
|
||||
#include <lib/core/CHIPError.h>
|
||||
#include <lib/core/DataModelTypes.h>
|
||||
@@ -34,6 +36,8 @@
|
||||
#include <zap-generated/access.h>
|
||||
#include <app/persistence/AttributePersistenceProviderInstance.h>
|
||||
#include <app/persistence/DefaultAttributePersistenceProvider.h>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
using namespace chip;
|
||||
using namespace chip::app;
|
||||
@@ -41,6 +45,206 @@ using namespace chip::app;
|
||||
constexpr char TAG[] = "DataModelProvider";
|
||||
|
||||
namespace {
|
||||
chip::Protocols::InteractionModel::Status BuildEmberBufferFromAttrVal(const esp_matter_attr_val_t &val,
|
||||
uint8_t *dataPtr, uint16_t bufferSize)
|
||||
{
|
||||
switch (val.type) {
|
||||
case ESP_MATTER_VAL_TYPE_BOOLEAN:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_BOOLEAN: {
|
||||
if (bufferSize < sizeof(bool) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<bool>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(*(uint8_t *)(&(val.val.b)))) {
|
||||
Traits::SetNull(*(uint8_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.b, *dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_FLOAT:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_FLOAT: {
|
||||
if (bufferSize < sizeof(float) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<float>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.f)) {
|
||||
Traits::SetNull(*(float *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.f, *(float *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_OCTET_STRING:
|
||||
case ESP_MATTER_VAL_TYPE_CHAR_STRING: {
|
||||
if (bufferSize < val.val.a.t || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
uint8_t len = val.val.a.s;
|
||||
size_t header_size = sizeof(len);
|
||||
if (bufferSize < header_size) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
memcpy(dataPtr, &len, header_size);
|
||||
// UINT8_MAX is reserved for null value
|
||||
if (len < UINT8_MAX) {
|
||||
size_t max_payload = bufferSize - header_size;
|
||||
size_t copy_len = (static_cast<size_t>(len) <= max_payload) ? static_cast<size_t>(len) : max_payload;
|
||||
memcpy(dataPtr + header_size, val.val.a.b, copy_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_LONG_OCTET_STRING:
|
||||
case ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING: {
|
||||
if (bufferSize < val.val.a.t || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
size_t header_size = sizeof(val.val.a.s);
|
||||
if (bufferSize < header_size) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
memcpy(dataPtr, &val.val.a.s, header_size);
|
||||
// UINT16_MAX is reserved for null value
|
||||
if (val.val.a.s < UINT16_MAX) {
|
||||
size_t max_payload = bufferSize - header_size;
|
||||
size_t copy_len = (static_cast<size_t>(val.val.a.s) <= max_payload)
|
||||
? static_cast<size_t>(val.val.a.s)
|
||||
: max_payload;
|
||||
memcpy(dataPtr + header_size, val.val.a.b, copy_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_INT8:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_INT8: {
|
||||
if (bufferSize < sizeof(int8_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<int8_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.i8)) {
|
||||
Traits::SetNull(*(int8_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.i8, *(int8_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_MATTER_VAL_TYPE_BITMAP8:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP8:
|
||||
case ESP_MATTER_VAL_TYPE_ENUM8:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM8:
|
||||
case ESP_MATTER_VAL_TYPE_UINT8:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT8: {
|
||||
if (bufferSize < sizeof(uint8_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<uint8_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.u8)) {
|
||||
Traits::SetNull(*dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.u8, *dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_INT16:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_INT16: {
|
||||
if (bufferSize < sizeof(int16_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<int16_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.i16)) {
|
||||
Traits::SetNull(*(int16_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.i16, *(int16_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_BITMAP16:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16:
|
||||
case ESP_MATTER_VAL_TYPE_ENUM16:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16:
|
||||
case ESP_MATTER_VAL_TYPE_UINT16:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT16: {
|
||||
if (bufferSize < sizeof(uint16_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<uint16_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.u16)) {
|
||||
Traits::SetNull(*(uint16_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.u16, *(uint16_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_INT32:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_INT32: {
|
||||
if (bufferSize < sizeof(int32_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<int32_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.i32)) {
|
||||
Traits::SetNull(*(int32_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.i32, *(int32_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_BITMAP32:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP32:
|
||||
case ESP_MATTER_VAL_TYPE_UINT32:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT32: {
|
||||
if (bufferSize < sizeof(uint32_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<uint32_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.u32)) {
|
||||
Traits::SetNull(*(uint32_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.u32, *(uint32_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_INT64:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_INT64: {
|
||||
if (bufferSize < sizeof(int64_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<int64_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.i64)) {
|
||||
Traits::SetNull(*(int64_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.i64, *(int64_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_MATTER_VAL_TYPE_UINT64:
|
||||
case ESP_MATTER_VAL_TYPE_NULLABLE_UINT64: {
|
||||
if (bufferSize < sizeof(uint64_t) || !dataPtr) {
|
||||
return chip::Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
}
|
||||
using Traits = chip::app::NumericAttributeTraits<uint64_t>;
|
||||
if ((val.type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val.val.u64)) {
|
||||
Traits::SetNull(*(uint64_t *)dataPtr);
|
||||
} else {
|
||||
Traits::WorkingToStorage(val.val.u64, *(uint64_t *)dataPtr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return chip::Protocols::InteractionModel::Status::InvalidDataType;
|
||||
}
|
||||
return chip::Protocols::InteractionModel::Status::Success;
|
||||
}
|
||||
|
||||
/// Attempts to read via an attribute access interface (AAI)
|
||||
///
|
||||
/// If it returns a CHIP_ERROR, then this is a FINAL result (i.e. either failure or success).
|
||||
@@ -305,17 +509,32 @@ ActionReturnStatus provider::WriteAttribute(const WriteAttributeRequest &request
|
||||
return *aai_result;
|
||||
}
|
||||
|
||||
const EmberAfAttributeMetadata *metadata =
|
||||
emberAfLocateAttributeMetadata(request.path.mEndpointId, request.path.mClusterId, request.path.mAttributeId);
|
||||
VerifyOrReturnValue(metadata != nullptr, Protocols::InteractionModel::Status::Failure);
|
||||
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(nullptr);
|
||||
VerifyOrReturnValue(attribute::get_val_internal(attribute, &val) == ESP_OK, Protocols::InteractionModel::Status::Failure);
|
||||
attribute_data_decode_buffer data_buffer(val.type);
|
||||
ReturnErrorOnFailure(decoder.Decode(data_buffer));
|
||||
esp_err_t err =
|
||||
attribute::update(request.path.mEndpointId, request.path.mClusterId, request.path.mAttributeId, &data_buffer.get_attr_val());
|
||||
if (err == ESP_ERR_NO_MEM) {
|
||||
return Protocols::InteractionModel::Status::ResourceExhausted;
|
||||
} else if (err != ESP_OK) {
|
||||
return Protocols::InteractionModel::Status::Failure;
|
||||
VerifyOrReturnValue(attribute::get_val_internal(attribute, &val) == ESP_OK,
|
||||
Protocols::InteractionModel::Status::Failure);
|
||||
attribute_data_decode_buffer decoded_buffer(val.type);
|
||||
ReturnErrorOnFailure(decoder.Decode(decoded_buffer));
|
||||
|
||||
std::vector<uint8_t> data_buff(metadata->size);
|
||||
Status buffer_status =
|
||||
BuildEmberBufferFromAttrVal(decoded_buffer.get_attr_val(), data_buff.data(),
|
||||
static_cast<uint16_t>(data_buff.size()));
|
||||
if (buffer_status != Protocols::InteractionModel::Status::Success) {
|
||||
return buffer_status;
|
||||
}
|
||||
|
||||
EmberAfWriteDataInput data_input(data_buff.data(), metadata->attributeType);
|
||||
data_input.SetChangeListener(&mContext->dataModelChangeListener);
|
||||
status = emberAfWriteAttribute(request.path, data_input);
|
||||
if (status != Protocols::InteractionModel::Status::Success) {
|
||||
return status;
|
||||
}
|
||||
// Increase data version and mark dirty
|
||||
cluster::increase_data_version(cluster);
|
||||
return Protocols::InteractionModel::Status::Success;
|
||||
}
|
||||
|
||||
|
||||
@@ -1019,6 +1019,33 @@ chip::Protocols::InteractionModel::Status emberAfReadAttribute(chip::EndpointId
|
||||
return get_raw_data_buffer_from_attr_val(val, dataPtr, readLength);
|
||||
}
|
||||
|
||||
chip::Protocols::InteractionModel::Status __attribute__((weak))
|
||||
MatterPreAttributeChangeCallback(const chip::app::ConcreteAttributePath &attributePath, uint8_t type, uint16_t size,
|
||||
uint8_t * value)
|
||||
{
|
||||
return chip::Protocols::InteractionModel::Status::Success;
|
||||
}
|
||||
|
||||
void __attribute__((weak))
|
||||
MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath &attributePath, uint8_t type, uint16_t size,
|
||||
uint8_t * value)
|
||||
{}
|
||||
|
||||
// This function is used to call the per-cluster pre-attribute changed callback
|
||||
Status emAfClusterPreAttributeChangedCallback(const chip::app::ConcreteAttributePath &attributePath, esp_matter::cluster_t *cluster, EmberAfAttributeType attributeType,
|
||||
uint16_t size, uint8_t * value)
|
||||
{
|
||||
Status status = Status::Success;
|
||||
esp_matter::cluster::function_pre_attribute_change_t f =
|
||||
(esp_matter::cluster::function_pre_attribute_change_t)esp_matter::cluster::get_function(
|
||||
cluster, esp_matter::CLUSTER_FLAG_PRE_ATTRIBUTE_CHANGED_FUNCTION);
|
||||
|
||||
if (f != nullptr) {
|
||||
status = f(attributePath, attributeType, size, value);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status emberAfWriteAttribute(chip::EndpointId endpointId, chip::ClusterId clusterId, chip::AttributeId attributeId,
|
||||
uint8_t *value, EmberAfAttributeType dataType)
|
||||
{
|
||||
@@ -1041,13 +1068,38 @@ Status emberAfWriteAttribute(const chip::app::ConcreteAttributePath &path, const
|
||||
if (!attribute) {
|
||||
return chip::Protocols::InteractionModel::Status::UnsupportedAttribute;
|
||||
}
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(nullptr);
|
||||
Status status = get_attr_val_from_raw_data_buffer(input.dataPtr, input.dataType, val,
|
||||
esp_matter::attribute::get_flags(attribute) &
|
||||
esp_matter::ATTRIBUTE_FLAG_NULLABLE);
|
||||
|
||||
const chip::app::ConcreteAttributePath attributePath(path.mEndpointId, path.mClusterId, path.mAttributeId);
|
||||
const EmberAfAttributeMetadata *metadata = emberAfLocateAttributeMetadata(path.mEndpointId, path.mClusterId, path.mAttributeId);
|
||||
if (!metadata) {
|
||||
return chip::Protocols::InteractionModel::Status::UnsupportedAttribute;
|
||||
}
|
||||
// Pre write attribute callback for all attribute changes, regardless of cluster.
|
||||
Status status = MatterPreAttributeChangeCallback(attributePath, input.dataType, emberAfAttributeSize(metadata), input.dataPtr);
|
||||
if (status != Status::Success) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Pre-write attribute callback specific to the cluster that the attribute lives in.
|
||||
status = emAfClusterPreAttributeChangedCallback(attributePath, cluster, input.dataType, emberAfAttributeSize(metadata), input.dataPtr);
|
||||
|
||||
// Ignore the following write operation and return success
|
||||
if (status == Status::WriteIgnored) {
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
if (status != Status::Success) {
|
||||
return status;
|
||||
}
|
||||
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(nullptr);
|
||||
status = get_attr_val_from_raw_data_buffer(input.dataPtr, input.dataType, val,
|
||||
esp_matter::attribute::get_flags(attribute) &
|
||||
esp_matter::ATTRIBUTE_FLAG_NULLABLE);
|
||||
if (status != Status::Success) {
|
||||
return status;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_matter::attribute::set_val_internal(attribute, &val);
|
||||
if (err != ESP_OK && err != ESP_ERR_NOT_FINISHED) {
|
||||
status = Status::Failure;
|
||||
@@ -1064,6 +1116,10 @@ Status emberAfWriteAttribute(const chip::app::ConcreteAttributePath &path, const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post write attribute callback for all attributes changes, regardless of cluster.
|
||||
MatterPostAttributeChangeCallback(attributePath, input.dataType, emberAfAttributeSize(metadata), input.dataPtr);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user