@@ -40,6 +40,27 @@ using namespace chip::app;
4040
4141constexpr char TAG[] = " DataModelProvider" ;
4242
43+ namespace chip {
44+ namespace app {
45+
46+ /* *
47+ * Accessor class for AttributeValueDecoder to get the TLV reader.
48+ * This leverages the existing friend declaration in the SDK's AttributeValueDecoder.
49+ */
50+ class TestOnlyAttributeValueDecoderAccessor
51+ {
52+ public:
53+ TestOnlyAttributeValueDecoderAccessor (AttributeValueDecoder & decoder) : mDecoder (decoder) {}
54+
55+ const TLV::TLVReader & GetReader () { return mDecoder .mReader ; }
56+
57+ private:
58+ AttributeValueDecoder & mDecoder ;
59+ };
60+
61+ } // namespace app
62+ } // namespace chip
63+
4364namespace {
4465// / Attempts to read via an attribute access interface (AAI)
4566// /
@@ -299,14 +320,51 @@ ActionReturnStatus provider::ReadAttribute(const ReadAttributeRequest &request,
299320
300321ActionReturnStatus provider::WriteAttribute (const WriteAttributeRequest &request, AttributeValueDecoder &decoder)
301322{
323+ attribute_t *attribute = attribute::get (request.path .mEndpointId , request.path .mClusterId ,
324+ request.path .mAttributeId );
325+
326+ // Decode the new value once - copy TLV reader to leave original decoder intact for mRegistry/AAI
327+ esp_matter_attr_val_t new_val = esp_matter_invalid (nullptr );
328+
329+ VerifyOrReturnValue (attribute, Protocols::InteractionModel::Status::UnsupportedAttribute);
330+
331+ esp_matter_val_type_t current_val_type = attribute::get_val_type (attribute);
332+
333+ TestOnlyAttributeValueDecoderAccessor accessor (decoder);
334+ TLV::TLVReader reader_copy;
335+ reader_copy.Init (accessor.GetReader ());
336+ AttributeValueDecoder decoder_copy (reader_copy, decoder.GetSubjectDescriptor ());
337+ attribute_data_decode_buffer data_buffer (current_val_type);
338+ if (decoder_copy.Decode (data_buffer) == CHIP_NO_ERROR) {
339+ new_val = data_buffer.get_attr_val ();
340+ // PRE_UPDATE callback
341+ if (attribute::execute_callback (attribute::PRE_UPDATE, request.path .mEndpointId ,
342+ request.path .mClusterId , request.path .mAttributeId ,
343+ &new_val) != ESP_OK) {
344+ return Protocols::InteractionModel::Status::Failure;
345+ }
346+ }
347+
348+ // Helper to execute POST_UPDATE callback
349+ auto execute_post_update = [&]() {
350+
351+ attribute::execute_callback (attribute::POST_UPDATE, request.path .mEndpointId ,
352+ request.path .mClusterId , request.path .mAttributeId , &new_val);
353+ };
354+
355+ // mRegistry handles its own storage
302356 if (auto *cluster = mRegistry .Get (request.path ); cluster != nullptr ) {
303- return cluster->WriteAttribute (request, decoder);
357+ ActionReturnStatus ret = cluster->WriteAttribute (request, decoder);
358+ if (ret.IsSuccess ()) {
359+ execute_post_update ();
360+ }
361+ return ret;
304362 }
363+
305364 Status status = CheckDataModelPath (request.path );
306365 VerifyOrReturnValue (status == Protocols::InteractionModel::Status::Success,
307366 CHIP_ERROR_IM_GLOBAL_STATUS_VALUE (status));
308367 cluster_t *cluster = cluster::get (request.path .mEndpointId , request.path .mClusterId );
309- attribute_t *attribute = attribute::get (cluster, request.path .mAttributeId );
310368 if (request.path .mDataVersion .HasValue ()) {
311369 chip::DataVersion data_version;
312370 VerifyOrReturnValue (cluster::get_data_version (cluster, data_version) == ESP_OK,
@@ -315,6 +373,7 @@ ActionReturnStatus provider::WriteAttribute(const WriteAttributeRequest &request
315373 Protocols::InteractionModel::Status::DataVersionMismatch);
316374 }
317375
376+ // AAI handles its own storage
318377 AttributeAccessInterface *aai =
319378 AttributeAccessInterfaceRegistry::Instance ().Get (request.path .mEndpointId , request.path .mClusterId );
320379 std::optional<CHIP_ERROR> aai_result = TryWriteViaAccessInterface (request.path , aai, decoder);
@@ -323,21 +382,23 @@ ActionReturnStatus provider::WriteAttribute(const WriteAttributeRequest &request
323382 cluster::increase_data_version (cluster);
324383 AttributePathParams path (request.path .mEndpointId , request.path .mClusterId , request.path .mAttributeId );
325384 mContext ->dataModelChangeListener .MarkDirty (path);
385+ execute_post_update ();
326386 }
327387 return *aai_result;
328388 }
329389
330- esp_matter_attr_val_t val = esp_matter_invalid (nullptr );
331- VerifyOrReturnValue (attribute::get_val_internal (attribute, &val) == ESP_OK, Protocols::InteractionModel::Status::Failure);
332- attribute_data_decode_buffer data_buffer (val.type );
333- ReturnErrorOnFailure (decoder.Decode (data_buffer));
334- esp_err_t err =
335- attribute::update (request.path .mEndpointId , request.path .mClusterId , request.path .mAttributeId , &data_buffer.get_attr_val ());
390+ // Use set_val_internal with call_callbacks=false since we already called PRE_UPDATE
391+ esp_err_t err = attribute::set_val_internal (attribute, &new_val, false );
336392 if (err == ESP_ERR_NO_MEM) {
337393 return Protocols::InteractionModel::Status::ResourceExhausted;
338- } else if (err != ESP_OK) {
394+ } else if (err != ESP_OK && err != ESP_ERR_NOT_FINISHED ) {
339395 return Protocols::InteractionModel::Status::Failure;
340396 }
397+ // Increase data version and mark dirty
398+ cluster::increase_data_version (cluster);
399+ AttributePathParams path (request.path .mEndpointId , request.path .mClusterId , request.path .mAttributeId );
400+ mContext ->dataModelChangeListener .MarkDirty (path);
401+ execute_post_update ();
341402 return Protocols::InteractionModel::Status::Success;
342403}
343404
0 commit comments