Remove _invokeCommandWithEndpointID implementation from MTRDevice. (#35208)

This is implemented (differently) by the different subclasses.

Once this implementation is removed, the following become unused and can be
removed:

* setExpectedValues
* removeExpectedValuesForAttributePaths

Once those are removed, the following become unused and can be removed:

* _getAttributesToReportWithNewExpectedValues
* _removeExpectedValueForAttributePath

Once those are removed, the following become unused and can be removed:

* _setExpectedValue
* expectedValueNextID

At this point (since setExpectedValues was removed), the only reference to
_checkExpiredExpectedValues is in _performScheduledExpirationCheck, which is
only called from _checkExpiredExpectedValues.  So these are both unreachable and
can be removed.  Then the following become unused and can be removed:

* expirationCheckScheduled
* _reportAttributes
* _attributeDataValue
* expectedValueCache

And then _filteredAttributes and _filteredEvents are unused and can be removed.

Then both the event and attribute versions of _interestedPaths: are unused and
can be removed.

At this point, the following become unused and can be removed:

* MTRDeviceExpectedValueFieldIndex
* MTRDeviceReadRequestFieldIndex
* MTRDeviceWriteRequestFieldIndex
* MTRDeviceWorkItemBatchingID
* MTRDeviceWorkItemDuplicateTypeID

------------------------------------------------------------
diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm
index 134042f..44aac3b 100644
--- a/src/darwin/Framework/CHIP/MTRDevice.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice.mm
@@ -111,33 +111,6 @@
 
 #pragma mark - MTRDevice
 
-typedef NS_ENUM(NSUInteger, MTRDeviceExpectedValueFieldIndex) {
-    MTRDeviceExpectedValueFieldExpirationTimeIndex = 0,
-    MTRDeviceExpectedValueFieldValueIndex = 1,
-    MTRDeviceExpectedValueFieldIDIndex = 2
-};
-
-typedef NS_ENUM(NSUInteger, MTRDeviceReadRequestFieldIndex) {
-    MTRDeviceReadRequestFieldPathIndex = 0,
-    MTRDeviceReadRequestFieldParamsIndex = 1
-};
-
-typedef NS_ENUM(NSUInteger, MTRDeviceWriteRequestFieldIndex) {
-    MTRDeviceWriteRequestFieldPathIndex = 0,
-    MTRDeviceWriteRequestFieldValueIndex = 1,
-    MTRDeviceWriteRequestFieldTimeoutIndex = 2,
-    MTRDeviceWriteRequestFieldExpectedValueIDIndex = 3,
-};
-
-typedef NS_ENUM(NSUInteger, MTRDeviceWorkItemBatchingID) {
-    MTRDeviceWorkItemBatchingReadID = 1,
-    MTRDeviceWorkItemBatchingWriteID = 2,
-};
-
-typedef NS_ENUM(NSUInteger, MTRDeviceWorkItemDuplicateTypeID) {
-    MTRDeviceWorkItemDuplicateReadTypeID = 1,
-};
-
 @implementation MTRDeviceClusterData {
     NSMutableDictionary<NSNumber *, MTRDeviceDataValueDictionary> * _attributes;
 }
@@ -238,20 +211,6 @@
 
 @end
 
-// Minimal time to wait since our last resubscribe failure before we will allow
-// a read attempt to prod our subscription.
-//
-// TODO: Figure out a better value for this, but for now don't allow this to
-// happen more often than once every 10 minutes.
-#define MTRDEVICE_MIN_RESUBSCRIBE_DUE_TO_READ_INTERVAL_SECONDS (10 * 60)
-
-// Weight of new data in determining subscription latencies.  To avoid random
-// outliers causing too much noise in the value, treat an existing value (if
-// any) as having 2/3 weight and the new value as having 1/3 weight.  These
-// weights are subject to change, if it's determined that different ones give
-// better behavior.
-#define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0)
-
 @interface MTRDevice ()
 // protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling,
 // and protects device calls to setUTCTime and setDSTOffset.  This can't just be replaced with "lock", because the time
@@ -267,21 +226,6 @@
 
 @property (nonatomic) MTRInternalDeviceState internalDeviceState;
 
-#define MTRDEVICE_SUBSCRIPTION_ATTEMPT_MIN_WAIT_SECONDS (1)
-#define MTRDEVICE_SUBSCRIPTION_ATTEMPT_MAX_WAIT_SECONDS (3600)
-@property (nonatomic) uint32_t lastSubscriptionAttemptWait;
-
-// Expected value cache is attributePath => NSArray of [NSDate of expiration time, NSDictionary of value, expected value ID]
-//   - See MTRDeviceExpectedValueFieldIndex for the definitions of indices into this array.
-// See MTRDeviceResponseHandler definition for value dictionary details.
-@property (nonatomic) NSMutableDictionary<MTRAttributePath *, NSArray *> * expectedValueCache;
-
-// This is a monotonically increasing value used when adding entries to expectedValueCache
-// Currently used/updated only in _getAttributesToReportWithNewExpectedValues:expirationTime:expectedValueID:
-@property (nonatomic) uint64_t expectedValueNextID;
-
-@property (nonatomic) BOOL expirationCheckScheduled;
-
 @property (nonatomic) BOOL timeUpdateScheduled;
 
 @property (nonatomic) NSMutableDictionary * temporaryMetaDataCache;
@@ -379,7 +323,6 @@
         _deviceController = controller;
         _queue
             = dispatch_queue_create("org.csa-iot.matter.framework.device.workqueue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
-        _expectedValueCache = [NSMutableDictionary dictionary];
         _asyncWorkQueue = [[MTRAsyncWorkQueue alloc] initWithContext:self];
         _state = MTRDeviceStateUnknown;
         _internalDeviceState = MTRInternalDeviceStateUnsubscribed;
@@ -1145,77 +1088,6 @@
     [self _resetStorageBehaviorState];
 }
 
-- (BOOL)_interestedPaths:(NSArray * _Nullable)interestedPaths includesAttributePath:(MTRAttributePath *)attributePath
-{
-    for (id interestedPath in interestedPaths) {
-        if ([interestedPath isKindOfClass:[NSNumber class]]) {
-            NSNumber * interestedEndpointIDNumber = interestedPath;
-            if ([interestedEndpointIDNumber isEqualToNumber:attributePath.endpoint]) {
-                return YES;
-            }
-        } else if ([interestedPath isKindOfClass:[MTRClusterPath class]]) {
-            MTRClusterPath * interestedClusterPath = interestedPath;
-            if ([interestedClusterPath.cluster isEqualToNumber:attributePath.cluster]) {
-                return YES;
-            }
-        } else if ([interestedPath isKindOfClass:[MTRAttributePath class]]) {
-            MTRAttributePath * interestedAttributePath = interestedPath;
-            if (([interestedAttributePath.cluster isEqualToNumber:attributePath.cluster]) && ([interestedAttributePath.attribute isEqualToNumber:attributePath.attribute])) {
-                return YES;
-            }
-        }
-    }
-
-    return NO;
-}
-
-// Returns filtered set of attributes using an interestedPaths array.
-// Returns nil if no attribute report has a path that matches the paths in the interestedPaths array.
-- (NSArray<NSDictionary<NSString *, id> *> *)_filteredAttributes:(NSArray<NSDictionary<NSString *, id> *> *)attributes forInterestedPaths:(NSArray * _Nullable)interestedPaths
-{
-    if (!interestedPaths) {
-        return attributes;
-    }
-
-    if (!interestedPaths.count) {
-        return nil;
-    }
-
-    NSMutableArray * filteredAttributes = nil;
-    for (NSDictionary<NSString *, id> * responseValue in attributes) {
-        MTRAttributePath * attributePath = responseValue[MTRAttributePathKey];
-        if ([self _interestedPaths:interestedPaths includesAttributePath:attributePath]) {
-            if (!filteredAttributes) {
-                filteredAttributes = [NSMutableArray array];
-            }
-            [filteredAttributes addObject:responseValue];
-        }
-    }
-
-    if (filteredAttributes.count && (filteredAttributes.count != attributes.count)) {
-        MTR_LOG("%@ filtered attribute report %lu => %lu", self, static_cast<unsigned long>(attributes.count), static_cast<unsigned long>(filteredAttributes.count));
-    }
-
-    return filteredAttributes;
-}
-
-// assume lock is held
-- (void)_reportAttributes:(NSArray<NSDictionary<NSString *, id> *> *)attributes
-{
-    os_unfair_lock_assert_owner(&self->_lock);
-    if (attributes.count) {
-        [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) {
-            // _iterateDelegatesWithBlock calls this with an autorelease pool, and so temporary filtered attributes reports don't bloat memory
-            NSArray<NSDictionary<NSString *, id> *> * filteredAttributes = [self _filteredAttributes:attributes forInterestedPaths:delegateInfo.interestedPathsForAttributes];
-            if (filteredAttributes.count) {
-                [delegateInfo callDelegateWithBlock:^(id<MTRDeviceDelegate> delegate) {
-                    [delegate device:self receivedAttributeReport:filteredAttributes];
-                }];
-            }
-        }];
-    }
-}
-
 #ifdef DEBUG
 - (void)unitTestInjectEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventReport
 {
@@ -1228,60 +1100,6 @@
 }
 #endif
 
-- (BOOL)_interestedPaths:(NSArray * _Nullable)interestedPaths includesEventPath:(MTREventPath *)eventPath
-{
-    for (id interestedPath in interestedPaths) {
-        if ([interestedPath isKindOfClass:[NSNumber class]]) {
-            NSNumber * interestedEndpointIDNumber = interestedPath;
-            if ([interestedEndpointIDNumber isEqualToNumber:eventPath.endpoint]) {
-                return YES;
-            }
-        } else if ([interestedPath isKindOfClass:[MTRClusterPath class]]) {
-            MTRClusterPath * interestedClusterPath = interestedPath;
-            if ([interestedClusterPath.cluster isEqualToNumber:eventPath.cluster]) {
-                return YES;
-            }
-        } else if ([interestedPath isKindOfClass:[MTREventPath class]]) {
-            MTREventPath * interestedEventPath = interestedPath;
-            if (([interestedEventPath.cluster isEqualToNumber:eventPath.cluster]) && ([interestedEventPath.event isEqualToNumber:eventPath.event])) {
-                return YES;
-            }
-        }
-    }
-
-    return NO;
-}
-
-// Returns filtered set of events using an interestedPaths array.
-// Returns nil if no event report has a path that matches the paths in the interestedPaths array.
-- (NSArray<NSDictionary<NSString *, id> *> *)_filteredEvents:(NSArray<NSDictionary<NSString *, id> *> *)events forInterestedPaths:(NSArray * _Nullable)interestedPaths
-{
-    if (!interestedPaths) {
-        return events;
-    }
-
-    if (!interestedPaths.count) {
-        return nil;
-    }
-
-    NSMutableArray * filteredEvents = nil;
-    for (NSDictionary<NSString *, id> * responseValue in events) {
-        MTREventPath * eventPath = responseValue[MTREventPathKey];
-        if ([self _interestedPaths:interestedPaths includesEventPath:eventPath]) {
-            if (!filteredEvents) {
-                filteredEvents = [NSMutableArray array];
-            }
-            [filteredEvents addObject:responseValue];
-        }
-    }
-
-    if (filteredEvents.count && (filteredEvents.count != events.count)) {
-        MTR_LOG("%@ filtered event report %lu => %lu", self, static_cast<unsigned long>(events.count), static_cast<unsigned long>(filteredEvents.count));
-    }
-
-    return filteredEvents;
-}
-
 #ifdef DEBUG
 - (void)unitTestClearClusterData
 {
@@ -1517,90 +1335,12 @@
                                queue:(dispatch_queue_t)queue
                           completion:(MTRDeviceResponseHandler)completion
 {
-    if (!expectedValueInterval || ([expectedValueInterval compare:@(0)] == NSOrderedAscending)) {
-        expectedValues = nil;
-    } else {
-        expectedValueInterval = MTRClampedNumber(expectedValueInterval, @(1), @(UINT32_MAX));
-    }
-
-    serverSideProcessingTimeout = [serverSideProcessingTimeout copy];
-    timeout = [timeout copy];
-
-    if (timeout == nil && MTRCommandNeedsTimedInvoke(clusterID, commandID)) {
-        timeout = @(MTR_DEFAULT_TIMED_INTERACTION_TIMEOUT_MS);
-    }
-
-    NSDate * cutoffTime;
-    if (timeout) {
-        cutoffTime = [NSDate dateWithTimeIntervalSinceNow:(timeout.doubleValue / 1000)];
-    }
-
-    uint64_t expectedValueID = 0;
-    NSMutableArray<MTRAttributePath *> * attributePaths = nil;
-    if (expectedValues) {
-        [self setExpectedValues:expectedValues expectedValueInterval:expectedValueInterval expectedValueID:&expectedValueID];
-        attributePaths = [NSMutableArray array];
-        for (NSDictionary<NSString *, id> * expectedValue in expectedValues) {
-            [attributePaths addObject:expectedValue[MTRAttributePathKey]];
-        }
-    }
-    MTRAsyncWorkItem * workItem = [[MTRAsyncWorkItem alloc] initWithQueue:self.queue];
-    uint64_t workItemID = workItem.uniqueID; // capture only the ID, not the work item
-    // The command operation will install a duplicate check handler, to return NO for "isDuplicate". Since a command operation may
-    // change values, only read requests after this should be considered for duplicate requests.
-    [workItem setDuplicateTypeID:MTRDeviceWorkItemDuplicateReadTypeID handler:^(id opaqueItemData, BOOL * isDuplicate, BOOL * stop) {
-        *isDuplicate = NO;
-        *stop = YES;
-    }];
-    [workItem setReadyHandler:^(MTRDevice * self, NSInteger retryCount, MTRAsyncWorkCompletionBlock workCompletion) {
-        auto workDone = ^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
-            dispatch_async(queue, ^{
-                completion(values, error);
-            });
-            if (error && expectedValues) {
-                [self removeExpectedValuesForAttributePaths:attributePaths expectedValueID:expectedValueID];
-            }
-            workCompletion(MTRAsyncWorkComplete);
-        };
-
-        NSNumber * timedInvokeTimeout = nil;
-        if (timeout) {
-            auto * now = [NSDate now];
-            if ([now compare:cutoffTime] == NSOrderedDescending) {
-                // Our timed invoke timeout has expired already.  Command
-                // was queued for too long.  Do not send it out.
-                workDone(nil, [MTRError errorForIMStatusCode:Status::Timeout]);
-                return;
-            }
-
-            // Recompute the actual timeout left, accounting for time spent
-            // in our queuing and retries.
-            timedInvokeTimeout = @([cutoffTime timeIntervalSinceDate:now] * 1000);
-        }
-        MTRBaseDevice * baseDevice = [self newBaseDevice];
-        [baseDevice
-            _invokeCommandWithEndpointID:endpointID
-                               clusterID:clusterID
-                               commandID:commandID
-                           commandFields:commandFields
-                      timedInvokeTimeout:timedInvokeTimeout
-             serverSideProcessingTimeout:serverSideProcessingTimeout
-                                   queue:self.queue
-                              completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
-                                  // Log the data at the INFO level (not usually persisted permanently),
-                                  // but make sure we log the work completion at the DEFAULT level.
-                                  MTR_LOG("Invoke work item [%llu] received command response: %@ error: %@", workItemID, values, error);
-                                  // TODO: This 5-retry cap is very arbitrary.
-                                  // TODO: Should there be some sort of backoff here?
-                                  if (error != nil && error.domain == MTRInteractionErrorDomain && error.code == MTRInteractionErrorCodeBusy && retryCount < 5) {
-                                      workCompletion(MTRAsyncWorkNeedsRetry);
-                                      return;
-                                  }
-
-                                  workDone(values, error);
-                              }];
-    }];
-    [_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"invoke %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, commandID.unsignedLongLongValue];
+#define MTRDeviceErrorStr "MTRDevice _invokeCommandWithEndpointID: must be handled by subclasses"
+    MTR_LOG_ERROR(MTRDeviceErrorStr);
+#ifdef DEBUG
+    NSAssert(NO, @MTRDeviceErrorStr);
+#endif // DEBUG
+#undef MTRDeviceErrorStr
 }
 
 - (void)_invokeKnownCommandWithEndpointID:(NSNumber *)endpointID
@@ -1692,93 +1432,6 @@
 
 #pragma mark - Cache management
 
-// assume lock is held
-- (void)_checkExpiredExpectedValues
-{
-    os_unfair_lock_assert_owner(&self->_lock);
-
-    // find expired attributes, and calculate next timer fire date
-    NSDate * now = [NSDate date];
-    NSDate * nextExpirationDate = nil;
-    // Set of NSArray with 2 elements [path, value] - this is used in this method only
-    NSMutableSet<NSArray *> * attributeInfoToRemove = [NSMutableSet set];
-    for (MTRAttributePath * attributePath in _expectedValueCache) {
-        NSArray * expectedValue = _expectedValueCache[attributePath];
-        NSDate * attributeExpirationDate = expectedValue[MTRDeviceExpectedValueFieldExpirationTimeIndex];
-        if (expectedValue) {
-            if ([now compare:attributeExpirationDate] == NSOrderedDescending) {
-                // expired - save [path, values] pair to attributeToRemove
-                [attributeInfoToRemove addObject:@[ attributePath, expectedValue[MTRDeviceExpectedValueFieldValueIndex] ]];
-            } else {
-                // get the next expiration date
-                if (!nextExpirationDate || [nextExpirationDate compare:attributeExpirationDate] == NSOrderedDescending) {
-                    nextExpirationDate = attributeExpirationDate;
-                }
-            }
-        }
-    }
-
-    // remove from expected value cache and report attributes as needed
-    NSMutableArray * attributesToReport = [NSMutableArray array];
-    NSMutableArray * attributePathsToReport = [NSMutableArray array];
-    for (NSArray * attributeInfo in attributeInfoToRemove) {
-        // compare with known value and mark for report if different
-        MTRAttributePath * attributePath = attributeInfo[0];
-        NSDictionary * attributeDataValue = attributeInfo[1];
-        NSDictionary * cachedAttributeDataValue = [self _cachedAttributeValueForPath:attributePath];
-        if (cachedAttributeDataValue
-            && ![self _attributeDataValue:attributeDataValue isEqualToDataValue:cachedAttributeDataValue]) {
-            [attributesToReport addObject:@{ MTRAttributePathKey : attributePath, MTRDataKey : cachedAttributeDataValue, MTRPreviousDataKey : attributeDataValue }];
-            [attributePathsToReport addObject:attributePath];
-        }
-
-        _expectedValueCache[attributePath] = nil;
-    }
-
-    // log attribute paths
-    MTR_LOG("%@ report from expired expected values %@", self, attributePathsToReport);
-    [self _reportAttributes:attributesToReport];
-
-// Have a reasonable minimum wait time for expiration timers
-#define MTR_DEVICE_EXPIRATION_CHECK_TIMER_MINIMUM_WAIT_TIME (0.1)
-
-    if (nextExpirationDate && _expectedValueCache.count && !self.expirationCheckScheduled) {
-        NSTimeInterval waitTime = [nextExpirationDate timeIntervalSinceDate:now];
-        if (waitTime < MTR_DEVICE_EXPIRATION_CHECK_TIMER_MINIMUM_WAIT_TIME) {
-            waitTime = MTR_DEVICE_EXPIRATION_CHECK_TIMER_MINIMUM_WAIT_TIME;
-        }
-        mtr_weakify(self);
-        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (waitTime * NSEC_PER_SEC)), self.queue, ^{
-            mtr_strongify(self);
-            [self _performScheduledExpirationCheck];
-        });
-    }
-}
-
-- (void)_performScheduledExpirationCheck
-{
-    std::lock_guard lock(_lock);
-
-    self.expirationCheckScheduled = NO;
-    [self _checkExpiredExpectedValues];
-}
-
-- (BOOL)_attributeDataValue:(NSDictionary *)one isEqualToDataValue:(NSDictionary *)theOther
-{
-    // Sanity check for nil cases
-    if (!one && !theOther) {
-        MTR_LOG_ERROR("%@ attribute data-value comparison does not expect comparing two nil dictionaries", self);
-        return YES;
-    }
-    if (!one || !theOther) {
-        // Comparing against nil is expected, and should return NO quietly
-        return NO;
-    }
-
-    // Attribute data-value dictionaries are equal if type and value are equal, and specifically, this should return true if values are both nil
-    return [one[MTRTypeKey] isEqual:theOther[MTRTypeKey]] && ((one[MTRValueKey] == theOther[MTRValueKey]) || [one[MTRValueKey] isEqual:theOther[MTRValueKey]]);
-}
-
 // Utility to return data value dictionary without data version
 - (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue
 {
@@ -1887,169 +1540,6 @@
     return _deviceCachePrimed;
 }
 
-// If value is non-nil, associate with expectedValueID
-// If value is nil, remove only if expectedValueID matches
-// previousValue is an out parameter
-- (void)_setExpectedValue:(NSDictionary<NSString *, id> *)expectedAttributeValue
-             attributePath:(MTRAttributePath *)attributePath
-            expirationTime:(NSDate *)expirationTime
-         shouldReportValue:(BOOL *)shouldReportValue
-    attributeValueToReport:(NSDictionary<NSString *, id> **)attributeValueToReport
-           expectedValueID:(uint64_t)expectedValueID
-             previousValue:(NSDictionary **)previousValue
-{
-    os_unfair_lock_assert_owner(&self->_lock);
-
-    *shouldReportValue = NO;
-
-    NSArray * previousExpectedValue = _expectedValueCache[attributePath];
-    if (previousExpectedValue) {
-        if (expectedAttributeValue
-            && ![self _attributeDataValue:expectedAttributeValue
-                       isEqualToDataValue:previousExpectedValue[MTRDeviceExpectedValueFieldValueIndex]]) {
-            // Case where new expected value overrides previous expected value - report new expected value
-            *shouldReportValue = YES;
-            *attributeValueToReport = expectedAttributeValue;
-            *previousValue = previousExpectedValue[MTRDeviceExpectedValueFieldValueIndex];
-        } else if (!expectedAttributeValue) {
-            // Remove previous expected value only if it's from the same setExpectedValues operation
-            NSNumber * previousExpectedValueID = previousExpectedValue[MTRDeviceExpectedValueFieldIDIndex];
-            if (previousExpectedValueID.unsignedLongLongValue == expectedValueID) {
-                MTRDeviceDataValueDictionary cachedValue = [self _cachedAttributeValueForPath:attributePath];
-                if (![self _attributeDataValue:previousExpectedValue[MTRDeviceExpectedValueFieldValueIndex]
-                            isEqualToDataValue:cachedValue]) {
-                    // Case of removing expected value that is different than read cache - report read cache value
-                    *shouldReportValue = YES;
-                    *attributeValueToReport = cachedValue;
-                    *previousValue = previousExpectedValue[MTRDeviceExpectedValueFieldValueIndex];
-                    _expectedValueCache[attributePath] = nil;
-                }
-            }
-        }
-    } else {
-        MTRDeviceDataValueDictionary cachedValue = [self _cachedAttributeValueForPath:attributePath];
-        if (expectedAttributeValue
-            && ![self _attributeDataValue:expectedAttributeValue isEqualToDataValue:cachedValue]) {
-            // Case where new expected value is different than read cache - report new expected value
-            *shouldReportValue = YES;
-            *attributeValueToReport = expectedAttributeValue;
-            *previousValue = cachedValue;
-        } else {
-            *previousValue = nil;
-        }
-
-        // No need to report if new and previous expected value are both nil
-    }
-
-    if (expectedAttributeValue) {
-        _expectedValueCache[attributePath] = @[ expirationTime, expectedAttributeValue, @(expectedValueID) ];
-    }
-}
-
-// assume lock is held
-- (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray<NSDictionary<NSString *, id> *> *)expectedAttributeValues
-                                          expirationTime:(NSDate *)expirationTime
-                                         expectedValueID:(uint64_t *)expectedValueID
-{
-    os_unfair_lock_assert_owner(&self->_lock);
-    uint64_t expectedValueIDToReturn = _expectedValueNextID++;
-
-    NSMutableArray * attributesToReport = [NSMutableArray array];
-    NSMutableArray * attributePathsToReport = [NSMutableArray array];
-    for (NSDictionary<NSString *, id> * attributeResponseValue in expectedAttributeValues) {
-        MTRAttributePath * attributePath = attributeResponseValue[MTRAttributePathKey];
-        NSDictionary * attributeDataValue = attributeResponseValue[MTRDataKey];
-
-        BOOL shouldReportValue = NO;
-        NSDictionary<NSString *, id> * attributeValueToReport;
-        NSDictionary<NSString *, id> * previousValue;
-        [self _setExpectedValue:attributeDataValue
-                     attributePath:attributePath
-                    expirationTime:expirationTime
-                 shouldReportValue:&shouldReportValue
-            attributeValueToReport:&attributeValueToReport
-                   expectedValueID:expectedValueIDToReturn
-                     previousValue:&previousValue];
-
-        if (shouldReportValue) {
-            if (previousValue) {
-                [attributesToReport addObject:@{ MTRAttributePathKey : attributePath, MTRDataKey : attributeValueToReport, MTRPreviousDataKey : previousValue }];
-            } else {
-                [attributesToReport addObject:@{ MTRAttributePathKey : attributePath, MTRDataKey : attributeValueToReport }];
-            }
-            [attributePathsToReport addObject:attributePath];
-        }
-    }
-    if (expectedValueID) {
-        *expectedValueID = expectedValueIDToReturn;
-    }
-
-    MTR_LOG("%@ report from new expected values %@", self, attributePathsToReport);
-
-    return attributesToReport;
-}
-
-// expectedValueID is an out-argument that returns an identifier to be used when removing expected values
-- (void)setExpectedValues:(NSArray<NSDictionary<NSString *, id> *> *)values
-    expectedValueInterval:(NSNumber *)expectedValueInterval
-          expectedValueID:(uint64_t *)expectedValueID
-{
-    // since NSTimeInterval is in seconds, convert ms into seconds in double
-    NSDate * expirationTime = [NSDate dateWithTimeIntervalSinceNow:expectedValueInterval.doubleValue / 1000];
-
-    MTR_LOG(
-        "%@ Setting expected values %@ with expiration time %f seconds from now", self, values, [expirationTime timeIntervalSinceNow]);
-
-    std::lock_guard lock(_lock);
-
-    // _getAttributesToReportWithNewExpectedValues will log attribute paths reported
-    NSArray * attributesToReport = [self _getAttributesToReportWithNewExpectedValues:values
-                                                                      expirationTime:expirationTime
-                                                                     expectedValueID:expectedValueID];
-    [self _reportAttributes:attributesToReport];
-
-    [self _checkExpiredExpectedValues];
-}
-
-- (void)removeExpectedValuesForAttributePaths:(NSArray<MTRAttributePath *> *)attributePaths
-                              expectedValueID:(uint64_t)expectedValueID
-{
-    std::lock_guard lock(_lock);
-
-    for (MTRAttributePath * attributePath in attributePaths) {
-        [self _removeExpectedValueForAttributePath:attributePath expectedValueID:expectedValueID];
-    }
-}
-
-- (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath expectedValueID:(uint64_t)expectedValueID
-{
-    os_unfair_lock_assert_owner(&self->_lock);
-
-    BOOL shouldReportValue;
-    NSDictionary<NSString *, id> * attributeValueToReport;
-    NSDictionary<NSString *, id> * previousValue;
-    [self _setExpectedValue:nil
-                 attributePath:attributePath
-                expirationTime:nil
-             shouldReportValue:&shouldReportValue
-        attributeValueToReport:&attributeValueToReport
-               expectedValueID:expectedValueID
-                 previousValue:&previousValue];
-
-    MTR_LOG("%@ remove expected value for path %@ should report %@", self, attributePath, shouldReportValue ? @"YES" : @"NO");
-
-    if (shouldReportValue) {
-        NSMutableDictionary * attribute = [NSMutableDictionary dictionaryWithObject:attributePath forKey:MTRAttributePathKey];
-        if (attributeValueToReport) {
-            attribute[MTRDataKey] = attributeValueToReport;
-        }
-        if (previousValue) {
-            attribute[MTRPreviousDataKey] = previousValue;
-        }
-        [self _reportAttributes:@[ attribute ]];
-    }
-}
-
 - (MTRBaseDevice *)newBaseDevice
 {
     return [MTRBaseDevice deviceWithNodeID:self.nodeID controller:self.deviceController];