Add read/subscribe event function for darwin (#24057)

* Add functions for darwin-framework-tool

Add functions for darwin-framework-tool
-discover commissionables
-pairing ethernet
-read-event-by-id

Added Matter.Framework APIs
MTRDeviceController
-discovercommissionableNodes
-setDeviceDiscoveryDelegate
MTRBaseDevice
-readEventsWithEndpointID

* Remove discovery and ethernet pairing changes

Discovery and ethernet pairing features are useful for test but
not mendatory for Matter certification.
So the removed codes will be committed after more verification.

* Update PairingCommandBridge.mm

* Update ReportCommandBridge.h

* Delete Commands.h

Removed because I think that this file seems not in pull request scope and it can be generated by using commands.zapt in this pull request.

* Rollback Commands.h

Rollback Commands.h to open source version.
I think that this file seems not in pull request scope and
it can be generated by using commands.zapt in this pull request.

* Update MTRDeviceController.h

* Update commands.zapt

Support any cluster for read-event-by-id of darwin-framework-tool

* Add Commands.h to fix zap build error

* Update Commands.h based on the latest version in upstream

* Remove fabric-filtered option in read-event-by-id

* Revert "Update Commands.h based on the latest version in upstream"

This reverts commit 8b80dd2d846e2927d7dac2721216bcf3273cf635.

* Restyled by whitespace

* Restyled by clang-format

* Restyle zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h

* Update Commands.h to fix zap-related error

* Added subscribeToEventsWithEndpointID

- Added event-min to ReadEvent

* Restyled by clang-format

* Added Read/SubscribeEvent to PowerSourceCluster

* Added BufferedReadClientCallback for both attribte and event

- Used ConcreteClusterPath and ValueId  instead of
  ConcreteAttribute/EventPath
- Removed BufferedReadEventCallback
- Added eventMin to MTRReadParams for EventFilter
- Added isUrgentEvent to MTRSubscribeParams for EventRequest
- Restyled by clang-format

* Modified to support 'any subscribe-event-by-id'

- Checked the invlalid id values are treated as wildcards in both
  subscribeToEventsWithEndpointID and readEventsWithEndpointID.
- Added suggested changes.

* Restyled by clang-format

* Removed the hardcoded true on subscribeWithQeuue

- Removed mEventNumber.SetValue on MTRSubscribeParams
- Added the wildcard handling as nil for Darwin API
- Removed the casts on descriptions

* Revert "Removed the casts on descriptions"

* Restore the mIsUrgentEvent in subscribeWithQueue.

Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: HyunKoo Ryu <hyunkoo.ryu@lge.com>
Co-authored-by: ready2die4u <combygod@gmail.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/examples/darwin-framework-tool/commands/clusters/ReportCommandBridge.h b/examples/darwin-framework-tool/commands/clusters/ReportCommandBridge.h
index 9b88ce5..ef834f6 100644
--- a/examples/darwin-framework-tool/commands/clusters/ReportCommandBridge.h
+++ b/examples/darwin-framework-tool/commands/clusters/ReportCommandBridge.h
@@ -185,6 +185,24 @@
     SubscribeEvent()
         : ModelCommand("subscribe-all-events")
     {
+        AddCommonArguments();
+    }
+
+    SubscribeEvent(chip::ClusterId clusterId, bool isClusterAny = false)
+        : ModelCommand("subscribe-event-by-id")
+        , mClusterId(clusterId)
+    {
+        if (isClusterAny == true) {
+            AddArgument("cluster-id", 0, UINT32_MAX, &mClusterId);
+        }
+        AddArgument("event-id", 0, UINT32_MAX, &mEventId);
+        AddArgument("event-min", 0, UINT64_MAX, &mEventNumber);
+        AddArgument("is-urgent", 0, 1, &mIsUrgent);
+        AddCommonArguments();
+    }
+
+    void AddCommonArguments()
+    {
         AddArgument("min-interval", 0, UINT16_MAX, &mMinInterval);
         AddArgument("max-interval", 0, UINT16_MAX, &mMaxInterval);
         AddArgument("keepSubscriptions", 0, 1, &mKeepSubscriptions);
@@ -199,34 +217,61 @@
         dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL);
 
         MTRSubscribeParams * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(mMinInterval) maxInterval:@(mMaxInterval)];
+        if (mEventNumber.HasValue()) {
+            params.minimumEventNumber = [NSNumber numberWithUnsignedLongLong:mEventNumber.Value()];
+        }
         if (mKeepSubscriptions.HasValue()) {
             params.replaceExistingSubscriptions = !mKeepSubscriptions.Value();
         }
+        if (mIsUrgent.HasValue()) {
+            params.reportEventsUrgently = mIsUrgent.Value();
+        }
         if (mAutoResubscribe.HasValue()) {
             params.resubscribeIfLost = mAutoResubscribe.Value();
         }
 
-        [device subscribeWithQueue:callbackQueue
-            params:params
-            clusterStateCacheContainer:nil
-            attributeReportHandler:^(NSArray * value) {
-                SetCommandExitStatus(CHIP_NO_ERROR);
-            }
-            eventReportHandler:^(NSArray * value) {
-                for (id item in value) {
-                    NSLog(@"Response Item: %@", [item description]);
+        if (strcmp(GetName(), "subscribe-event-by-id") == 0) {
+            [device subscribeToEventsWithEndpointID:(endpointId == chip::kInvalidEndpointId)
+                        ? nil
+                        : [NSNumber numberWithUnsignedShort:endpointId]
+                clusterID:(mClusterId == chip::kInvalidClusterId) ? nil : [NSNumber numberWithUnsignedInteger:mClusterId]
+                eventID:(mEventId == chip::kInvalidEventId) ? nil : [NSNumber numberWithUnsignedInteger:mEventId]
+                params:params
+                queue:callbackQueue
+                reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
+                    if (values) {
+                        for (id item in values) {
+                            NSLog(@"Response Item: %@", [item description]);
+                        }
+                    }
+                    SetCommandExitStatus(error);
                 }
-                SetCommandExitStatus(CHIP_NO_ERROR);
-            }
-            errorHandler:^(NSError * error) {
-                SetCommandExitStatus(error);
-            }
-            subscriptionEstablished:^() {
-                mSubscriptionEstablished = YES;
-            }
-            resubscriptionScheduled:^(NSError * error, NSNumber * resubscriptionDelay) {
-                NSLog(@"Subscription dropped with error %@.  Resubscription in %@ms", error, resubscriptionDelay);
-            }];
+                subscriptionEstablished:^() {
+                    mSubscriptionEstablished = YES;
+                }];
+        } else {
+            [device subscribeWithQueue:callbackQueue
+                params:params
+                clusterStateCacheContainer:nil
+                attributeReportHandler:^(NSArray * value) {
+                    SetCommandExitStatus(CHIP_NO_ERROR);
+                }
+                eventReportHandler:^(NSArray * value) {
+                    for (id item in value) {
+                        NSLog(@"Response Item: %@", [item description]);
+                    }
+                    SetCommandExitStatus(CHIP_NO_ERROR);
+                }
+                errorHandler:^(NSError * error) {
+                    SetCommandExitStatus(error);
+                }
+                subscriptionEstablished:^() {
+                    mSubscriptionEstablished = YES;
+                }
+                resubscriptionScheduled:^(NSError * error, NSNumber * resubscriptionDelay) {
+                    NSLog(@"Subscription dropped with error %@.  Resubscription in %@ms", error, resubscriptionDelay);
+                }];
+        }
 
         return CHIP_NO_ERROR;
     }
@@ -237,7 +282,82 @@
     chip::Optional<bool> mKeepSubscriptions;
     chip::Optional<bool> mAutoResubscribe;
     chip::Optional<chip::EventNumber> mEventNumber;
+    chip::Optional<bool> mIsUrgent;
     bool mSubscriptionEstablished = NO;
     uint16_t mMinInterval;
     uint16_t mMaxInterval;
+
+    void Shutdown() override
+    {
+        mSubscriptionEstablished = NO;
+        ModelCommand::Shutdown();
+    }
+
+    bool DeferInteractiveCleanup() override { return mSubscriptionEstablished; }
+
+private:
+    chip::ClusterId mClusterId;
+    chip::EventId mEventId;
+};
+
+class ReadEvent : public ModelCommand {
+public:
+    ReadEvent()
+        : ModelCommand("read-event-by-id")
+    {
+        AddArgument("cluster-id", 0, UINT32_MAX, &mClusterId);
+        AddArgument("event-id", 0, UINT32_MAX, &mEventId);
+        AddArgument("event-min", 0, UINT64_MAX, &mEventNumber);
+        ModelCommand::AddArguments();
+    }
+
+    ReadEvent(chip::ClusterId clusterId)
+        : ModelCommand("read-event-by-id")
+        , mClusterId(clusterId)
+    {
+        AddArgument("event-id", 0, UINT32_MAX, &mEventId);
+        AddArgument("event-min", 0, UINT64_MAX, &mEventNumber);
+        ModelCommand::AddArguments();
+    }
+
+    ~ReadEvent() {}
+
+    CHIP_ERROR SendCommand(MTRBaseDevice * _Nonnull device, chip::EndpointId endpointId) override
+    {
+        dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.command", DISPATCH_QUEUE_SERIAL);
+        MTRReadParams * params = [[MTRReadParams alloc] init];
+        if (mFabricFiltered.HasValue()) {
+            params.filterByFabric = mFabricFiltered.Value();
+        }
+        if (mEventNumber.HasValue()) {
+            params.minimumEventNumber = [NSNumber numberWithUnsignedLongLong:mEventNumber.Value()];
+        }
+
+        [device
+            readEventsWithEndpointID:(endpointId == chip::kInvalidEndpointId) ? nil : [NSNumber numberWithUnsignedShort:endpointId]
+                           clusterID:(mClusterId == chip::kInvalidClusterId) ? nil : [NSNumber numberWithUnsignedInteger:mClusterId]
+                             eventID:(mEventId == chip::kInvalidEventId) ? nil : [NSNumber numberWithUnsignedInteger:mEventId]
+                              params:params
+                               queue:callbackQueue
+                          completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
+                              if (error != nil) {
+                                  LogNSError("Error reading event", error);
+                              }
+                              if (values) {
+                                  for (id item in values) {
+                                      NSLog(@"Response Item: %@", [item description]);
+                                  }
+                              }
+                              SetCommandExitStatus(error);
+                          }];
+        return CHIP_NO_ERROR;
+    }
+
+protected:
+    chip::Optional<bool> mFabricFiltered;
+    chip::Optional<chip::EventNumber> mEventNumber;
+
+private:
+    chip::ClusterId mClusterId;
+    chip::AttributeId mEventId;
 };
diff --git a/examples/darwin-framework-tool/templates/commands.zapt b/examples/darwin-framework-tool/templates/commands.zapt
index 0b6f736..75c8acb 100644
--- a/examples/darwin-framework-tool/templates/commands.zapt
+++ b/examples/darwin-framework-tool/templates/commands.zapt
@@ -292,6 +292,12 @@
         {{/if}}
         {{/unless}}
         {{/chip_server_cluster_attributes}}
+        {{#zcl_events}}
+        {{#first}}
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
+        {{/first}}
+        {{/zcl_events}}
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -308,6 +314,8 @@
         make_unique<ReadAttribute>(),   //
         make_unique<WriteAttribute>(),  //
         make_unique<SubscribeAttribute>(), //
+        make_unique<ReadEvent>(),     //
+        make_unique<SubscribeEvent>(chip::kInvalidClusterId, true), //
         make_unique<SubscribeEvent>(),     //
     };
 
diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.h b/src/darwin/Framework/CHIP/MTRBaseDevice.h
index c93a7e4..9bc630d 100644
--- a/src/darwin/Framework/CHIP/MTRBaseDevice.h
+++ b/src/darwin/Framework/CHIP/MTRBaseDevice.h
@@ -315,6 +315,47 @@
                                       completion:(MTRDeviceOpenCommissioningWindowHandler)completion
     API_AVAILABLE(ios(16.2), macos(13.1), watchos(9.2), tvos(16.2));
 
+/**
+ * Reads events from the device.
+ *
+ * Nil values for endpointID, clusterID, eventID indicate wildcards
+ * (e.g. nil eventID means "read all the events from the endpoint(s) and
+ * cluster(s) that match endpointID/clusterID").
+ *
+ * If all of endpointID, clusterID, eventID are non-nil, all the matching instances of a single
+ * event will be read.
+ *
+ * If all of endpointID, clusterID, eventID are nil, all events on the
+ * device will be read.
+ */
+
+- (void)readEventsWithEndpointID:(NSNumber * _Nullable)endpointID
+                       clusterID:(NSNumber * _Nullable)clusterID
+                         eventID:(NSNumber * _Nullable)eventID
+                          params:(MTRReadParams * _Nullable)params
+                           queue:(dispatch_queue_t)queue
+                      completion:(MTRDeviceResponseHandler)completion MTR_NEWLY_AVAILABLE;
+
+/**
+ * Subscribes to the specified events on the device.
+ *
+ * Nil values for endpointID, clusterID, eventID indicate wildcards
+ * (e.g. nil eventID means "subscribe to all the events from the
+ * endpoint(s) and cluster(s) that match endpointID/clusterID").
+ *
+ * If all of endpointID, clusterID, eventID are non-nil, a single event
+ * will be subscribed to.
+ *
+ * If all of endpointID, clusterID, eventID are nil, all events on the
+ * device will be subscribed to.
+ */
+- (void)subscribeToEventsWithEndpointID:(NSNumber * _Nullable)endpointID
+                              clusterID:(NSNumber * _Nullable)clusterID
+                                eventID:(NSNumber * _Nullable)eventID
+                                 params:(MTRSubscribeParams * _Nullable)params
+                                  queue:(dispatch_queue_t)queue
+                          reportHandler:(MTRDeviceResponseHandler)reportHandler
+                subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished MTR_NEWLY_AVAILABLE;
 @end
 
 /**
diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm
index 3a36030..65651b7 100644
--- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm
+++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm
@@ -30,6 +30,7 @@
 
 #include "app/ConcreteAttributePath.h"
 #include "app/ConcreteCommandPath.h"
+#include "app/ConcreteEventPath.h"
 #include "lib/core/CHIPError.h"
 #include "lib/core/DataModelTypes.h"
 
@@ -79,6 +80,7 @@
 @interface MTRReadClientContainer : NSObject
 @property (nonatomic, readwrite) app::ReadClient * readClientPtr;
 @property (nonatomic, readwrite) app::AttributePathParams * pathParams;
+@property (nonatomic, readwrite) app::EventPathParams * eventPathParams;
 @property (nonatomic, readwrite) uint64_t deviceID;
 - (void)onDone;
 @end
@@ -335,8 +337,7 @@
                                       // Wildcard endpoint, cluster, attribute, event.
                                       auto attributePath = std::make_unique<AttributePathParams>();
                                       auto eventPath = std::make_unique<EventPathParams>();
-                                      // We want to get event reports at the minInterval, not the maxInterval.
-                                      eventPath->mIsUrgentEvent = true;
+                                      eventPath->mIsUrgentEvent = params.reportEventsUrgently;
                                       ReadPrepareParams readParams(session.Value());
                                       [params toReadPrepareParams:readParams];
                                       readParams.mpAttributePathParamsList = attributePath.get();
@@ -727,19 +728,20 @@
     static void OnSuccessFn(void * context, id value) { DispatchSuccess(context, value); }
 };
 
-template <typename DecodableAttributeType> class BufferedReadAttributeCallback final : public app::ReadClient::Callback {
+template <typename DecodableValueType> class BufferedReadClientCallback final : public app::ReadClient::Callback {
 public:
     using OnSuccessCallbackType
-        = std::function<void(const app::ConcreteDataAttributePath & aPath, const DecodableAttributeType & aData)>;
-    using OnErrorCallbackType = std::function<void(const app::ConcreteDataAttributePath * aPath, CHIP_ERROR aError)>;
-    using OnDoneCallbackType = std::function<void(BufferedReadAttributeCallback * callback)>;
+        = std::function<void(const app::ConcreteClusterPath & aPath, const uint32_t aValueId, const DecodableValueType & aData)>;
+    using OnErrorCallbackType
+        = std::function<void(const app::ConcreteClusterPath * aPath, const uint32_t aValueId, CHIP_ERROR aError)>;
+    using OnDoneCallbackType = std::function<void(BufferedReadClientCallback * callback)>;
     using OnSubscriptionEstablishedCallbackType = std::function<void()>;
 
-    BufferedReadAttributeCallback(ClusterId aClusterId, AttributeId aAttributeId, OnSuccessCallbackType aOnSuccess,
+    BufferedReadClientCallback(ClusterId aClusterId, uint32_t aValueId, OnSuccessCallbackType aOnSuccess,
         OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone,
         OnSubscriptionEstablishedCallbackType aOnSubscriptionEstablished = nullptr)
         : mClusterId(aClusterId)
-        , mAttributeId(aAttributeId)
+        , mValueId(aValueId)
         , mOnSuccess(aOnSuccess)
         , mOnError(aOnError)
         , mOnDone(aOnDone)
@@ -748,7 +750,7 @@
     {
     }
 
-    ~BufferedReadAttributeCallback()
+    ~BufferedReadClientCallback()
     {
         // Ensure we release the ReadClient before we tear down anything else,
         // so it can call our OnDeallocatePaths properly.
@@ -764,7 +766,7 @@
         const app::ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const app::StatusIB & aStatus) override
     {
         CHIP_ERROR err = CHIP_NO_ERROR;
-        DecodableAttributeType value;
+        DecodableValueType value;
 
         //
         // We shouldn't be getting list item operations in the provided path since that should be handled by the buffered read
@@ -774,21 +776,41 @@
 
         VerifyOrExit(aStatus.IsSuccess(), err = aStatus.ToChipError());
         VerifyOrExit((aPath.mClusterId == mClusterId || mClusterId == kInvalidClusterId)
-                && (aPath.mAttributeId == mAttributeId || mAttributeId == kInvalidAttributeId),
+                && (aPath.mAttributeId == mValueId || mValueId == kInvalidAttributeId),
             err = CHIP_ERROR_SCHEMA_MISMATCH);
         VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
 
         SuccessOrExit(err = app::DataModel::Decode(*apData, value));
 
-        mOnSuccess(aPath, value);
+        mOnSuccess(aPath, aPath.mAttributeId, value);
 
     exit:
         if (err != CHIP_NO_ERROR) {
-            mOnError(&aPath, err);
+            mOnError(&aPath, aPath.mAttributeId, err);
         }
     }
 
-    void OnError(CHIP_ERROR aError) override { mOnError(nullptr, aError); }
+    void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) override
+    {
+        CHIP_ERROR err = CHIP_NO_ERROR;
+        DecodableValueType value;
+
+        VerifyOrExit((aEventHeader.mPath.mClusterId == mClusterId || mClusterId == kInvalidClusterId)
+                && (aEventHeader.mPath.mEventId == mValueId || mValueId == kInvalidEventId),
+            err = CHIP_ERROR_SCHEMA_MISMATCH);
+        VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
+
+        SuccessOrExit(err = app::DataModel::Decode(*apData, value));
+
+        mOnSuccess(aEventHeader.mPath, aEventHeader.mPath.mEventId, value);
+
+    exit:
+        if (err != CHIP_NO_ERROR) {
+            mOnError(&aEventHeader.mPath, aEventHeader.mPath.mEventId, err);
+        }
+    }
+
+    void OnError(CHIP_ERROR aError) override { mOnError(nullptr, kInvalidAttributeId, aError); }
 
     void OnDone(ReadClient *) override { mOnDone(this); }
 
@@ -802,7 +824,7 @@
     void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override {}
 
     ClusterId mClusterId;
-    AttributeId mAttributeId;
+    uint32_t mValueId;
     OnSuccessCallbackType mOnSuccess;
     OnErrorCallbackType mOnError;
     OnDoneCallbackType mOnDone;
@@ -828,8 +850,9 @@
             auto resultArray = [[NSMutableArray alloc] init];
             auto resultSuccess = [[NSMutableArray alloc] init];
             auto resultFailure = [[NSMutableArray alloc] init];
-            auto onSuccessCb = [resultArray, resultSuccess](const app::ConcreteAttributePath & attribPath,
+            auto onSuccessCb = [resultArray, resultSuccess](const app::ConcreteClusterPath & clusterPath, const uint32_t aValueId,
                                    const MTRDataValueDictionaryDecodableType & aData) {
+                app::ConcreteAttributePath attribPath(clusterPath.mEndpointId, clusterPath.mClusterId, aValueId);
                 [resultArray addObject:@ {
                     MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:attribPath],
                     MTRDataKey : aData.GetDecodedObject()
@@ -839,10 +862,12 @@
                 }
             };
 
-            auto onFailureCb = [resultArray, resultFailure](const app::ConcreteAttributePath * attribPath, CHIP_ERROR aError) {
-                if (attribPath) {
+            auto onFailureCb = [resultArray, resultFailure](
+                                   const app::ConcreteClusterPath * clusterPath, const uint32_t aValueId, CHIP_ERROR aError) {
+                if (clusterPath) {
+                    app::ConcreteAttributePath attribPath(clusterPath->mEndpointId, clusterPath->mClusterId, aValueId);
                     [resultArray addObject:@ {
-                        MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:*attribPath],
+                        MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:attribPath],
                         MTRErrorKey : [MTRError errorForCHIPErrorCode:aError]
                     }];
                 } else if ([resultFailure count] == 0) {
@@ -869,7 +894,7 @@
             readParams.mAttributePathParamsListSize = 1;
 
             auto onDone = [resultArray, resultSuccess, resultFailure, bridge, successCb, failureCb](
-                              BufferedReadAttributeCallback<MTRDataValueDictionaryDecodableType> * callback) {
+                              BufferedReadClientCallback<MTRDataValueDictionaryDecodableType> * callback) {
                 if ([resultFailure count] > 0 || [resultSuccess count] == 0) {
                     // Failure
                     if (failureCb) {
@@ -890,7 +915,7 @@
                 chip::Platform::Delete(callback);
             };
 
-            auto callback = chip::Platform::MakeUnique<BufferedReadAttributeCallback<MTRDataValueDictionaryDecodableType>>(
+            auto callback = chip::Platform::MakeUnique<BufferedReadClientCallback<MTRDataValueDictionaryDecodableType>>(
                 attributePath.mClusterId, attributePath.mAttributeId, onSuccessCb, onFailureCb, onDone, nullptr);
             VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
 
@@ -1181,10 +1206,10 @@
                        return;
                    }
 
-                   auto onReportCb = [queue, reportHandler](const app::ConcreteAttributePath & attribPath,
+                   auto onReportCb = [queue, reportHandler](const app::ConcreteClusterPath & clusterPath, const uint32_t aValueId,
                                          const MTRDataValueDictionaryDecodableType & data) {
                        id valueObject = data.GetDecodedObject();
-                       app::ConcreteAttributePath pathCopy = attribPath;
+                       app::ConcreteAttributePath pathCopy(clusterPath.mEndpointId, clusterPath.mClusterId, aValueId);
                        dispatch_async(queue, ^{
                            reportHandler(@[ @ {
                                MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:pathCopy],
@@ -1196,7 +1221,7 @@
 
                    auto establishedOrFailed = chip::Platform::MakeShared<BOOL>(NO);
                    auto onFailureCb = [establishedOrFailed, queue, subscriptionEstablished, reportHandler](
-                                          const app::ConcreteAttributePath * attribPath, CHIP_ERROR error) {
+                                          const app::ConcreteClusterPath * clusterPath, const uint32_t aValueId, CHIP_ERROR error) {
                        if (!(*establishedOrFailed)) {
                            *establishedOrFailed = YES;
                            if (subscriptionEstablished) {
@@ -1241,14 +1266,14 @@
                    readParams.mpAttributePathParamsList = container.pathParams;
                    readParams.mAttributePathParamsListSize = 1;
 
-                   auto onDone = [container](BufferedReadAttributeCallback<MTRDataValueDictionaryDecodableType> * callback) {
+                   auto onDone = [container](BufferedReadClientCallback<MTRDataValueDictionaryDecodableType> * callback) {
                        [container onDone];
                        // Make sure we delete callback last, because doing that actually destroys our
                        // lambda, so we can't access captured values after that.
                        chip::Platform::Delete(callback);
                    };
 
-                   auto callback = chip::Platform::MakeUnique<BufferedReadAttributeCallback<MTRDataValueDictionaryDecodableType>>(
+                   auto callback = chip::Platform::MakeUnique<BufferedReadClientCallback<MTRDataValueDictionaryDecodableType>>(
                        container.pathParams->mClusterId, container.pathParams->mAttributeId, onReportCb, onFailureCb, onDone,
                        onEstablishedCb);
 
@@ -1475,6 +1500,246 @@
     return decodedData.GetDecodedObject();
 }
 
+- (void)readEventsWithEndpointID:(NSNumber * _Nullable)endpointID
+                       clusterID:(NSNumber * _Nullable)clusterID
+                         eventID:(NSNumber * _Nullable)eventID
+                          params:(MTRReadParams * _Nullable)params
+                           queue:(dispatch_queue_t)queue
+                      completion:(MTRDeviceResponseHandler)completion
+{
+    endpointID = (endpointID == nil) ? nil : [endpointID copy];
+    clusterID = (clusterID == nil) ? nil : [clusterID copy];
+    eventID = (eventID == nil) ? nil : [eventID copy];
+    params = (params == nil) ? nil : [params copy];
+    auto * bridge = new MTRDataValueDictionaryCallbackBridge(queue, completion,
+        ^(ExchangeManager & exchangeManager, const SessionHandle & session, MTRDataValueDictionaryCallback successCb,
+            MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
+            auto resultArray = [[NSMutableArray alloc] init];
+            auto resultSuccess = [[NSMutableArray alloc] init];
+            auto resultFailure = [[NSMutableArray alloc] init];
+            auto onSuccessCb = [resultArray, resultSuccess](const app::ConcreteClusterPath & clusterPath, const uint32_t aValueId,
+                                   const MTRDataValueDictionaryDecodableType & aData) {
+                app::ConcreteEventPath eventPath(clusterPath.mEndpointId, clusterPath.mClusterId, aValueId);
+                [resultArray addObject:@ {
+                    MTREventPathKey : [[MTREventPath alloc] initWithPath:eventPath],
+                    MTRDataKey : aData.GetDecodedObject()
+                }];
+                if ([resultSuccess count] == 0) {
+                    [resultSuccess addObject:[NSNumber numberWithBool:YES]];
+                }
+            };
+
+            auto onFailureCb = [resultArray, resultFailure](
+                                   const app::ConcreteClusterPath * clusterPath, const uint32_t aValueId, CHIP_ERROR aError) {
+                if (clusterPath) {
+                    app::ConcreteEventPath eventPath(clusterPath->mEndpointId, clusterPath->mClusterId, aValueId);
+                    [resultArray addObject:@ {
+                        MTREventPathKey : [[MTREventPath alloc] initWithPath:eventPath],
+                        MTRErrorKey : [MTRError errorForCHIPErrorCode:aError]
+                    }];
+                } else if ([resultFailure count] == 0) {
+                    [resultFailure addObject:[MTRError errorForCHIPErrorCode:aError]];
+                }
+            };
+
+            app::EventPathParams eventPath;
+            if (endpointID) {
+                eventPath.mEndpointId = static_cast<chip::EndpointId>([endpointID unsignedShortValue]);
+            }
+            if (clusterID) {
+                eventPath.mClusterId = static_cast<chip::ClusterId>([clusterID unsignedLongValue]);
+            }
+            if (eventID) {
+                eventPath.mEventId = static_cast<chip::EventId>([eventID unsignedLongValue]);
+            }
+            app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
+            CHIP_ERROR err = CHIP_NO_ERROR;
+
+            chip::app::ReadPrepareParams readParams(session);
+            [params toReadPrepareParams:readParams];
+            readParams.mpEventPathParamsList = &eventPath;
+            readParams.mEventPathParamsListSize = 1;
+
+            auto onDone = [resultArray, resultSuccess, resultFailure, bridge, successCb, failureCb](
+                              BufferedReadClientCallback<MTRDataValueDictionaryDecodableType> * callback) {
+                if ([resultFailure count] > 0 || [resultSuccess count] == 0) {
+                    // Failure
+                    if (failureCb) {
+                        if ([resultFailure count] > 0) {
+                            failureCb(bridge, [MTRError errorToCHIPErrorCode:resultFailure[0]]);
+                        } else if ([resultArray count] > 0) {
+                            failureCb(bridge, [MTRError errorToCHIPErrorCode:resultArray[0][MTRErrorKey]]);
+                        } else {
+                            failureCb(bridge, CHIP_ERROR_READ_FAILED);
+                        }
+                    }
+                } else {
+                    // Success
+                    if (successCb) {
+                        successCb(bridge, resultArray);
+                    }
+                }
+                chip::Platform::Delete(callback);
+            };
+
+            auto callback = chip::Platform::MakeUnique<BufferedReadClientCallback<MTRDataValueDictionaryDecodableType>>(
+                eventPath.mClusterId, eventPath.mEventId, onSuccessCb, onFailureCb, onDone, nullptr);
+            VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
+
+            auto readClient = chip::Platform::MakeUnique<app::ReadClient>(
+                engine, &exchangeManager, callback->GetBufferedCallback(), chip::app::ReadClient::InteractionType::Read);
+            VerifyOrReturnError(readClient != nullptr, CHIP_ERROR_NO_MEMORY);
+
+            err = readClient->SendRequest(readParams);
+
+            if (err != CHIP_NO_ERROR) {
+                return err;
+            }
+
+            //
+            // At this point, we'll get a callback through the OnDone callback above regardless of success or failure
+            // of the read operation to permit us to free up the callback object. So, release ownership of the callback
+            // object now to prevent it from being reclaimed at the end of this scoped block.
+            //
+            callback->AdoptReadClient(std::move(readClient));
+            callback.release();
+            return err;
+        });
+    std::move(*bridge).DispatchAction(self);
+}
+
+- (void)subscribeToEventsWithEndpointID:(NSNumber * _Nullable)endpointID
+                              clusterID:(NSNumber * _Nullable)clusterID
+                                eventID:(NSNumber * _Nullable)eventID
+                                 params:(MTRSubscribeParams * _Nullable)params
+                                  queue:(dispatch_queue_t)queue
+                          reportHandler:(MTRDeviceResponseHandler)reportHandler
+                subscriptionEstablished:(MTRSubscriptionEstablishedHandler)subscriptionEstablished
+{
+    if (self.isPASEDevice) {
+        // We don't support subscriptions over PASE.
+        dispatch_async(queue, ^{
+            reportHandler(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]);
+        });
+        return;
+    }
+
+    // Copy params before going async.
+    endpointID = (endpointID == nil) ? nil : [endpointID copy];
+    clusterID = (clusterID == nil) ? nil : [clusterID copy];
+    eventID = (eventID == nil) ? nil : [eventID copy];
+    params = (params == nil) ? nil : [params copy];
+
+    [self.deviceController
+        getSessionForNode:self.nodeID
+               completion:^(ExchangeManager * _Nullable exchangeManager, const Optional<SessionHandle> & session,
+                   NSError * _Nullable error) {
+                   if (error != nil) {
+                       if (reportHandler) {
+                           dispatch_async(queue, ^{
+                               reportHandler(nil, error);
+                           });
+                       }
+                       return;
+                   }
+
+                   auto onReportCb = [queue, reportHandler](const app::ConcreteClusterPath & clusterPath, const uint32_t aValueId,
+                                         const MTRDataValueDictionaryDecodableType & data) {
+                       id valueObject = data.GetDecodedObject();
+                       app::ConcreteEventPath pathCopy(clusterPath.mEndpointId, clusterPath.mClusterId, aValueId);
+                       dispatch_async(queue, ^{
+                           reportHandler(
+                               @[ @ { MTREventPathKey : [[MTREventPath alloc] initWithPath:pathCopy], MTRDataKey : valueObject } ],
+                               nil);
+                       });
+                   };
+
+                   auto establishedOrFailed = chip::Platform::MakeShared<BOOL>(NO);
+                   auto onFailureCb = [establishedOrFailed, queue, subscriptionEstablished, reportHandler](
+                                          const app::ConcreteClusterPath * clusterPath, const uint32_t aValueId, CHIP_ERROR error) {
+                       if (!(*establishedOrFailed)) {
+                           *establishedOrFailed = YES;
+                           if (subscriptionEstablished) {
+                               dispatch_async(queue, subscriptionEstablished);
+                           }
+                       }
+                       if (reportHandler) {
+                           dispatch_async(queue, ^{
+                               reportHandler(nil, [MTRError errorForCHIPErrorCode:error]);
+                           });
+                       }
+                   };
+
+                   auto onEstablishedCb = [establishedOrFailed, queue, subscriptionEstablished]() {
+                       if (*establishedOrFailed) {
+                           return;
+                       }
+                       *establishedOrFailed = YES;
+                       if (subscriptionEstablished) {
+                           dispatch_async(queue, subscriptionEstablished);
+                       }
+                   };
+
+                   MTRReadClientContainer * container = [[MTRReadClientContainer alloc] init];
+                   container.deviceID = self.nodeID;
+                   container.eventPathParams = Platform::New<app::EventPathParams>();
+                   if (endpointID) {
+                       container.eventPathParams->mEndpointId = static_cast<chip::EndpointId>([endpointID unsignedShortValue]);
+                   }
+                   if (clusterID) {
+                       container.eventPathParams->mClusterId = static_cast<chip::ClusterId>([clusterID unsignedLongValue]);
+                   }
+                   if (eventID) {
+                       container.eventPathParams->mEventId = static_cast<chip::EventId>([eventID unsignedLongValue]);
+                   }
+                   container.eventPathParams->mIsUrgentEvent = params.reportEventsUrgently;
+
+                   app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
+                   CHIP_ERROR err = CHIP_NO_ERROR;
+
+                   chip::app::ReadPrepareParams readParams(session.Value());
+                   [params toReadPrepareParams:readParams];
+                   readParams.mpEventPathParamsList = container.eventPathParams;
+                   readParams.mEventPathParamsListSize = 1;
+
+                   auto onDone = [container](BufferedReadClientCallback<MTRDataValueDictionaryDecodableType> * callback) {
+                       [container onDone];
+                       // Make sure we delete callback last, because doing that actually destroys our
+                       // lambda, so we can't access captured values after that.
+                       chip::Platform::Delete(callback);
+                   };
+
+                   auto callback = chip::Platform::MakeUnique<BufferedReadClientCallback<MTRDataValueDictionaryDecodableType>>(
+                       container.eventPathParams->mClusterId, container.eventPathParams->mEventId, onReportCb, onFailureCb, onDone,
+                       onEstablishedCb);
+
+                   auto readClient = Platform::New<app::ReadClient>(
+                       engine, exchangeManager, callback->GetBufferedCallback(), chip::app::ReadClient::InteractionType::Subscribe);
+
+                   if (!params.resubscribeIfLost) {
+                       err = readClient->SendRequest(readParams);
+                   } else {
+                       err = readClient->SendAutoResubscribeRequest(std::move(readParams));
+                   }
+
+                   if (err != CHIP_NO_ERROR) {
+                       if (reportHandler) {
+                           dispatch_async(queue, ^{
+                               reportHandler(nil, [MTRError errorForCHIPErrorCode:err]);
+                           });
+                       }
+                       Platform::Delete(readClient);
+                       Platform::Delete(container.eventPathParams);
+                       container.eventPathParams = nullptr;
+                       return;
+                   }
+
+                   // Read clients will be purged when deregistered.
+                   container.readClientPtr = readClient;
+                   AddReadClientContainer(container.deviceID, container);
+                   callback.release();
+               }];
+}
 @end
 
 @implementation MTRBaseDevice (Deprecated)
@@ -1707,6 +1972,13 @@
     return self;
 }
 
+- (NSString *)description
+{
+    return
+        [NSString stringWithFormat:@"<MTREventPath> endpoint %u cluster %u event %u", (uint16_t) self.endpoint.unsignedShortValue,
+                  (uint32_t) self.cluster.unsignedLongValue, (uint32_t) _event.unsignedLongValue];
+}
+
 + (instancetype)eventPathWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID eventID:(NSNumber *)eventID
 {
     ConcreteEventPath path(static_cast<chip::EndpointId>([endpointID unsignedShortValue]),
diff --git a/src/darwin/Framework/CHIP/MTRCluster.h b/src/darwin/Framework/CHIP/MTRCluster.h
index 8b07be1..bfe42b0 100644
--- a/src/darwin/Framework/CHIP/MTRCluster.h
+++ b/src/darwin/Framework/CHIP/MTRCluster.h
@@ -77,7 +77,7 @@
 
 /**
  * MTRReadParams
- *    This is used to control the behavior of attribute reads and subscribes.
+ *    This is used to control the behavior of attribute/event reads and subscribes.
  *    If not provided (i.e. nil passed for the MTRReadParams argument), will be
  *    treated as if a default-initialized object was passed in.
  */
@@ -94,11 +94,20 @@
  */
 @property (nonatomic, assign, getter=shouldFilterByFabric) BOOL filterByFabric MTR_NEWLY_AVAILABLE;
 
+/**
+ * Sets a filter for which events will be reported in the read/subscribe interaction.
+ *
+ * If nil (the default value), all of the queued events will be reported from lowest to highest event number.
+ *
+ * If not nil, queued events with an event number smaller than minimumEventNumber will not be reported.
+ */
+@property (nonatomic, copy, nullable) NSNumber * minimumEventNumber MTR_NEWLY_AVAILABLE;
+
 @end
 
 /**
  * MTRSubscribeParams
- *    This is used to control the behavior of attribute subscribes.  If not
+ *    This is used to control the behavior of attribute/event subscribes.  If not
  *    provided (i.e. nil passed for the MTRSubscribeParams argument), will be
  *    treated as if a default-initialized object was passed in.
  */
@@ -145,6 +154,15 @@
 @property (nonatomic, copy) NSNumber * maxInterval;
 
 /**
+ * Controls whether events will be reported urgently. The default value is YES.
+ *
+ * If YES, the events will be reported as soon as the minInterval does not prevent it.
+ *
+ * If NO, the events will be reported at the maximum interval.
+ */
+@property (nonatomic, assign) BOOL reportEventsUrgently MTR_NEWLY_AVAILABLE;
+
+/**
  * Initialize an MTRSubscribeParams.  Must provide a minInterval and
  * maxInterval; there are no default values for those.
  */
diff --git a/src/darwin/Framework/CHIP/MTRCluster.mm b/src/darwin/Framework/CHIP/MTRCluster.mm
index 1f7faec..042b3a7 100644
--- a/src/darwin/Framework/CHIP/MTRCluster.mm
+++ b/src/darwin/Framework/CHIP/MTRCluster.mm
@@ -74,12 +74,16 @@
 {
     auto other = [[MTRReadParams alloc] init];
     other.filterByFabric = self.filterByFabric;
+    other.minimumEventNumber = self.minimumEventNumber;
     return other;
 }
 
 - (void)toReadPrepareParams:(chip::app::ReadPrepareParams &)readPrepareParams
 {
     readPrepareParams.mIsFabricFiltered = self.filterByFabric;
+    if (self.minimumEventNumber) {
+        readPrepareParams.mEventNumber.SetValue(static_cast<chip::EventNumber>([self.minimumEventNumber unsignedLongLongValue]));
+    }
 }
 
 @end
@@ -88,6 +92,7 @@
 - (instancetype)initWithMinInterval:(NSNumber *)minInterval maxInterval:(NSNumber *)maxInterval
 {
     if (self = [super init]) {
+        _reportEventsUrgently = YES;
         _replaceExistingSubscriptions = YES;
         _resubscribeIfLost = YES;
         _minInterval = [minInterval copy];
@@ -100,7 +105,9 @@
 {
     auto other = [[MTRSubscribeParams alloc] initWithMinInterval:self.minInterval maxInterval:self.maxInterval];
     other.filterByFabric = self.filterByFabric;
+    other.minimumEventNumber = self.minimumEventNumber;
     other.replaceExistingSubscriptions = self.replaceExistingSubscriptions;
+    other.reportEventsUrgently = self.reportEventsUrgently;
     other.resubscribeIfLost = self.resubscribeIfLost;
     return other;
 }
diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h
index d34afdb..1363330 100644
--- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h
+++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h
@@ -97854,6 +97854,8 @@
         make_unique<SubscribeAttributeAccessControlFeatureMap>(), //
         make_unique<ReadAccessControlClusterRevision>(), //
         make_unique<SubscribeAttributeAccessControlClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -97897,6 +97899,8 @@
         make_unique<SubscribeAttributeActionsFeatureMap>(), //
         make_unique<ReadActionsClusterRevision>(), //
         make_unique<SubscribeAttributeActionsClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -97965,6 +97969,8 @@
         make_unique<SubscribeAttributeBasicInformationFeatureMap>(), //
         make_unique<ReadBasicInformationClusterRevision>(), //
         make_unique<SubscribeAttributeBasicInformationClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98028,6 +98034,8 @@
         make_unique<SubscribeAttributeOtaSoftwareUpdateRequestorFeatureMap>(), //
         make_unique<ReadOtaSoftwareUpdateRequestorClusterRevision>(), //
         make_unique<SubscribeAttributeOtaSoftwareUpdateRequestorClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98233,6 +98241,8 @@
         make_unique<SubscribeAttributePowerSourceFeatureMap>(), //
         make_unique<ReadPowerSourceClusterRevision>(), //
         make_unique<SubscribeAttributePowerSourceClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98390,6 +98400,8 @@
         make_unique<SubscribeAttributeGeneralDiagnosticsFeatureMap>(), //
         make_unique<ReadGeneralDiagnosticsClusterRevision>(), //
         make_unique<SubscribeAttributeGeneralDiagnosticsClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98424,6 +98436,8 @@
         make_unique<SubscribeAttributeSoftwareDiagnosticsFeatureMap>(), //
         make_unique<ReadSoftwareDiagnosticsClusterRevision>(), //
         make_unique<SubscribeAttributeSoftwareDiagnosticsClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98576,6 +98590,8 @@
         make_unique<SubscribeAttributeThreadNetworkDiagnosticsFeatureMap>(), //
         make_unique<ReadThreadNetworkDiagnosticsClusterRevision>(), //
         make_unique<SubscribeAttributeThreadNetworkDiagnosticsClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98628,6 +98644,8 @@
         make_unique<SubscribeAttributeWiFiNetworkDiagnosticsFeatureMap>(), //
         make_unique<ReadWiFiNetworkDiagnosticsClusterRevision>(), //
         make_unique<SubscribeAttributeWiFiNetworkDiagnosticsClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98728,6 +98746,8 @@
         make_unique<SubscribeAttributeBridgedDeviceBasicInformationFeatureMap>(), //
         make_unique<ReadBridgedDeviceBasicInformationClusterRevision>(), //
         make_unique<SubscribeAttributeBridgedDeviceBasicInformationClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98759,6 +98779,8 @@
         make_unique<SubscribeAttributeSwitchFeatureMap>(), //
         make_unique<ReadSwitchClusterRevision>(), //
         make_unique<SubscribeAttributeSwitchClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -98958,6 +98980,8 @@
         make_unique<SubscribeAttributeBooleanStateFeatureMap>(), //
         make_unique<ReadBooleanStateClusterRevision>(), //
         make_unique<SubscribeAttributeBooleanStateClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -99131,6 +99155,8 @@
         make_unique<SubscribeAttributeDoorLockFeatureMap>(), //
         make_unique<ReadDoorLockClusterRevision>(), //
         make_unique<SubscribeAttributeDoorLockClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -99336,6 +99362,8 @@
         make_unique<SubscribeAttributePumpConfigurationAndControlFeatureMap>(), //
         make_unique<ReadPumpConfigurationAndControlClusterRevision>(), //
         make_unique<SubscribeAttributePumpConfigurationAndControlClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -101011,6 +101039,8 @@
         make_unique<SubscribeAttributeUnitTestingFeatureMap>(), //
         make_unique<ReadUnitTestingClusterRevision>(), //
         make_unique<SubscribeAttributeUnitTestingClusterRevision>(), //
+        make_unique<ReadEvent>(Id), //
+        make_unique<SubscribeEvent>(Id), //
     };
 
     commands.Register(clusterName, clusterCommands);
@@ -101025,6 +101055,8 @@
         make_unique<ReadAttribute>(), //
         make_unique<WriteAttribute>(), //
         make_unique<SubscribeAttribute>(), //
+        make_unique<ReadEvent>(), //
+        make_unique<SubscribeEvent>(chip::kInvalidClusterId, true), //
         make_unique<SubscribeEvent>(), //
     };