blob: 10d0b9f8e1393d3c39a6dd38635e7a090aff8235 [file] [log] [blame]
/**
* Copyright (c) 2024 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <Matter/MTRError.h>
#import "MTRAttributeValueWaiter_Internal.h"
#import "MTRDevice_Internal.h"
#import "MTRLogging_Internal.h"
@implementation MTRAwaitedAttributeState
- (instancetype)initWithValue:(MTRDeviceDataValueDictionary)value
{
if (self = [super init]) {
_valueSatisfied = NO;
_value = value;
}
return self;
}
@end
MTR_DIRECT_MEMBERS
@interface MTRAttributeValueWaiter ()
@property (nonatomic, retain) NSDictionary<MTRAttributePath *, MTRAwaitedAttributeState *> * valueExpectations;
// Protected by the MTRDevice's lock.
@property (nonatomic, readwrite, retain) dispatch_queue_t queue;
@property (nonatomic, readwrite, copy, nullable) MTRStatusCompletion completion;
@property (nonatomic, readonly, retain) MTRDevice * device;
@end
@implementation MTRAttributeValueWaiter
- (instancetype)initWithDevice:(MTRDevice *)device values:(NSDictionary<MTRAttributePath *, MTRDeviceDataValueDictionary> *)values queue:(dispatch_queue_t)queue completion:(MTRStatusCompletion)completion
{
if (self = [super init]) {
auto * valueExpectations = [NSMutableDictionary dictionaryWithCapacity:values.count];
for (MTRAttributePath * path in values) {
auto * valueExpectation = [[MTRAwaitedAttributeState alloc] initWithValue:values[path]];
valueExpectations[path] = valueExpectation;
}
_valueExpectations = valueExpectations;
_queue = queue;
_completion = completion;
_device = device;
_UUID = [NSUUID UUID];
}
return self;
}
- (void)dealloc
{
[self cancel];
}
- (void)cancel
{
[self.device _attributeWaitCanceled:self];
}
- (BOOL)_attributeValue:(MTRDeviceDataValueDictionary)value reportedForPath:(MTRAttributePath *)path byDevice:(MTRDevice *)device
{
MTRAwaitedAttributeState * valueExpectation = self.valueExpectations[path];
if (!valueExpectation) {
// We don't care about this one.
return NO;
}
MTRDeviceDataValueDictionary expectedValue = valueExpectation.value;
valueExpectation.valueSatisfied = [device _attributeDataValue:value satisfiesValueExpectation:expectedValue];
return valueExpectation.valueSatisfied;
}
- (BOOL)allValuesSatisfied
{
for (MTRAwaitedAttributeState * valueExpectation in [self.valueExpectations allValues]) {
if (!valueExpectation.valueSatisfied) {
return NO;
}
}
return YES;
}
- (void)_notifyWithError:(NSError * _Nullable)error
{
// This is always called with the device lock held, so checking and mutating
// self.completion here is atomic.
if (!self.completion) {
return;
}
if (self.expirationTimer != nil) {
dispatch_source_cancel(self.expirationTimer);
self.expirationTimer = nil;
}
if (!error) {
MTR_LOG("%@ %p wait for attribute values completed", self, self);
} else if (error.domain == MTRErrorDomain && error.code == MTRErrorCodeTimeout) {
MTR_LOG("%@ %p wait for attribute values timed out", self, self);
} else if (error.domain == MTRErrorDomain && error.code == MTRErrorCodeCancelled) {
MTR_LOG("%@ %p wait for attribute values canceled", self, self);
} else {
MTR_LOG("%@ %p wait for attribute values unknown error: %@", self, self, error);
}
auto completion = self.completion;
auto queue = self.queue;
self.completion = nil;
self.queue = nil;
dispatch_async(queue, ^{
completion(error);
});
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %@>", NSStringFromClass(self.class), self.UUID];
}
@end