blob: 70ccaf691470346403200d7ed7cf4fb3b3fdcd23 [file] [log] [blame]
{{> header excludeZapComment=true}}
#import <Foundation/Foundation.h>
#import "MTRAsyncCallbackWorkQueue.h"
#import "MTRBaseDevice_Internal.h"
#import "MTRClusterConstants.h"
#import "MTRClusters_Internal.h"
#import "MTRDevice_Internal.h"
#import "MTRCallbackBridge.h"
#import "MTRCluster_Internal.h"
#import "MTRStructsObjc.h"
#import "MTRCommandPayloadsObjc.h"
#import "MTRLogging_Internal.h"
#include <lib/support/CHIPListUtils.h>
#include <platform/CHIPDeviceLayer.h>
#include <type_traits>
using chip::Callback::Callback;
using chip::Callback::Cancelable;
using namespace chip::app::Clusters;
using chip::Messaging::ExchangeManager;
using chip::SessionHandle;
static void MTRClustersLogEnqueue(NSString *logPrefix, MTRAsyncCallbackWorkQueue *workQueue) {
MTR_LOG_INFO("%@ enqueueWorkItem %@", logPrefix, workQueue);
}
static void MTRClustersLogDequeue(NSString *logPrefix, MTRAsyncCallbackWorkQueue *workQueue) {
MTR_LOG_INFO("%@ dequeueWorkItem %@", logPrefix, workQueue);
}
static void MTRClustersLogCompletion(NSString *logPrefix, id value, NSError *error) {
MTR_LOG_INFO("%@ completion value %@ error %@ endWork", logPrefix, value, error);
}
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks): Linter is unable to locate the delete on these objects.
{{#chip_client_clusters includeAll=true}}
@implementation MTRCluster{{asUpperCamelCase name preserveAcronyms=true}}
- (instancetype)initWithDevice:(MTRDevice *)device endpointID:(NSNumber *)endpointID queue:(dispatch_queue_t)queue
{
if (self = [super initWithQueue:queue]) {
if (device == nil) {
return nil;
}
{{!TODO consider range-checking the incoming number to make sure it's
actually in the uint16_t range}}
_endpoint = [endpointID unsignedShortValue];
_device = device;
}
return self;
}
{{#chip_cluster_commands}}
{{! Takes two arguments: cluster name and command name, plus the ambient state where the command is "this" }}
{{#*inline "commandImpl"}}
{{! This is used as the implementation for both the new-name and old-name bits, so check for both here. }}
{{#unless (and (wasRemoved cluster command=command)
(wasRemoved (compatClusterNameRemapping parent.name) command=(compatCommandNameRemapping parent.name name)))}}
{{#*inline "callbackName"}}{{#if hasSpecificResponse}}{{cluster}}Cluster{{asUpperCamelCase responseName preserveAcronyms=true}}{{else}}CommandSuccess{{/if}}{{/inline}}
{{#*inline "paramsType"}}
{{#if (wasRemoved cluster command=command)}}
MTR{{compatClusterNameRemapping parent.name}}Cluster{{compatCommandNameRemapping parent.name name}}Params
{{else}}
MTR{{cluster}}Cluster{{command}}Params
{{/if}}
{{/inline}}
{{#*inline "clusterId"}}
{{#if (wasRemoved cluster command=command)}}
{{asMEI ../manufacturerCode ../code}}
{{else}}
MTRClusterIDType{{cluster}}ID
{{/if}}
{{/inline}}
{{#*inline "commandId"}}
{{#if (wasRemoved cluster command=command)}}
{{asMEI manufacturerCode code}}
{{else}}
MTRCommandIDTypeCluster{{cluster}}Command{{command}}ID
{{/if}}
{{/inline}}
{{#unless (hasArguments)}}
- (void){{asLowerCamelCase name}}WithExpectedValues:(NSArray<NSDictionary<NSString *, id> *> *)expectedValues expectedValueInterval:(NSNumber *)expectedValueIntervalMs completion:({{>command_completion_type command=.}})completion
{
[self {{asLowerCamelCase name}}WithParams:nil expectedValues:expectedValues expectedValueInterval:expectedValueIntervalMs completion:completion];
}
{{/unless}}
- (void){{asLowerCamelCase name}}WithParams: ({{> paramsType}} * {{#unless (commandHasRequiredField .)}}_Nullable{{/unless}})params expectedValues:(NSArray<NSDictionary<NSString *, id> *> *)expectedValues expectedValueInterval:(NSNumber *)expectedValueIntervalMs completion:({{>command_completion_type command=.}})completion
{
NSString * logPrefix = [NSString stringWithFormat:@"MTRDevice command %u %u %u %u", self.device.deviceController.fabricIndex, _endpoint, (unsigned int){{> clusterId}}, (unsigned int){{> commandId}}];
// Make a copy of params before we go async.
params = [params copy];
NSNumber *timedInvokeTimeoutMsParam = params.timedInvokeTimeoutMs;
if (timedInvokeTimeoutMsParam) {
timedInvokeTimeoutMsParam = MTRClampedNumber(timedInvokeTimeoutMsParam, @(1), @(UINT16_MAX));
}
MTRAsyncCallbackQueueWorkItem * workItem = [[MTRAsyncCallbackQueueWorkItem alloc] initWithQueue:self.device.queue];
MTRAsyncCallbackReadyHandler readyHandler = ^(MTRDevice * device, NSUInteger retryCount) {
MTRClustersLogDequeue(logPrefix, self.device.asyncCallbackWorkQueue);
MTRBaseDevice *baseDevice = [[MTRBaseDevice alloc] initWithNodeID:self.device.nodeID controller:self.device.deviceController];
auto * bridge = new MTR{{>callbackName}}CallbackBridge(self.device.queue,
^(id _Nullable value, NSError * _Nullable error) {
MTRClustersLogCompletion(logPrefix, value, error);
dispatch_async(self.callbackQueue, ^{
{{#if hasSpecificResponse}}
{{! This treats completion as taking an id for the data. This is
not great from a type-safety perspective, of course. }}
completion(value, error);
{{else}}
{{! For now, don't change the bridge API; instead just use an adapter
to invoke our completion handler. This is not great from a
type-safety perspective, of course. }}
completion(error);
{{/if}}
});
[workItem endWork];
},
^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>callbackName}}CallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
chip::Optional<uint16_t> timedInvokeTimeoutMs;
ListFreer listFreer;
{{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::Type request;
if (timedInvokeTimeoutMsParam != nil) {
timedInvokeTimeoutMs.SetValue(timedInvokeTimeoutMsParam.unsignedShortValue);
}
{{#if mustUseTimedInvoke}}
if (!timedInvokeTimeoutMs.HasValue()) {
timedInvokeTimeoutMs.SetValue(10000);
}
{{/if}}
{{#chip_cluster_command_arguments}}
{{#first}}
{{#unless (commandHasRequiredField parent)}}
if (params != nil) {
{{/unless}}
{{/first}}
{{>encode_value target=(concat "request." (asLowerCamelCase label)) source=(concat "params." (asStructPropertyName label)) cluster=parent.parent.name errorCode="return CHIP_ERROR_INVALID_ARGUMENT;" depth=0}}
{{#last}}
{{#unless (commandHasRequiredField parent)}}
}
{{/unless}}
{{/last}}
{{/chip_cluster_command_arguments}}
chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint);
return cppCluster.InvokeCommand(request, bridge, successCb, failureCb, timedInvokeTimeoutMs);
});
std::move(*bridge).DispatchAction(baseDevice);
};
workItem.readyHandler = readyHandler;
MTRClustersLogEnqueue(logPrefix, self.device.asyncCallbackWorkQueue);
[self.device.asyncCallbackWorkQueue enqueueWorkItem:workItem];
if (!expectedValueIntervalMs || ([expectedValueIntervalMs compare:@(0)] == NSOrderedAscending)) {
expectedValues = nil;
} else {
expectedValueIntervalMs = MTRClampedNumber(expectedValueIntervalMs, @(1), @(UINT32_MAX));
}
if (expectedValues) {
[self.device setExpectedValues:expectedValues expectedValueInterval:expectedValueIntervalMs];
}
}
{{/unless}}
{{/inline}}
{{> commandImpl cluster=(asUpperCamelCase parent.name preserveAcronyms=true)
command=(asUpperCamelCase name preserveAcronyms=true)}}
{{/chip_cluster_commands}}
{{#chip_server_cluster_attributes}}
{{#*inline "cluster"}}{{asUpperCamelCase parent.name preserveAcronyms=true}}{{/inline}}
{{#*inline "attribute"}}Attribute{{asUpperCamelCase name preserveAcronyms=true}}{{/inline}}
- (NSDictionary<NSString *, id> *)read{{>attribute}}WithParams:(MTRReadParams * _Nullable)params {
return [self.device readAttributeWithEndpointID:@(_endpoint) clusterID:@(MTRClusterIDType{{>cluster}}ID) attributeID:@(MTRAttributeIDTypeCluster{{>cluster}}{{>attribute}}ID) params:params];
}
{{#if isWritableAttribute}}
{{#*inline "callbackName"}}DefaultSuccess{{/inline}}
- (void)write{{>attribute}}WithValue:(NSDictionary<NSString *, id> *)dataValueDictionary expectedValueInterval:(NSNumber *)expectedValueIntervalMs
{
[self write{{>attribute}}WithValue:dataValueDictionary expectedValueInterval:expectedValueIntervalMs params:nil];
}
- (void)write{{>attribute}}WithValue:(NSDictionary<NSString *, id> *)dataValueDictionary expectedValueInterval:(NSNumber *)expectedValueIntervalMs params:(MTRWriteParams * _Nullable)params
{
NSNumber *timedWriteTimeout = params.timedWriteTimeout;
{{#if mustUseTimedInvoke}}
if (!timedWriteTimeout) {
timedWriteTimeout = @(10000);
}
{{/if}}
[self.device writeAttributeWithEndpointID:@(_endpoint) clusterID:@(MTRClusterIDType{{>cluster}}ID) attributeID:@(MTRAttributeIDTypeCluster{{>cluster}}{{>attribute}}ID) value:dataValueDictionary expectedValueInterval:expectedValueIntervalMs timedWriteTimeout:timedWriteTimeout];
}
{{/if}}
{{/chip_server_cluster_attributes}}
@end
{{#unless (isStrEqual (asUpperCamelCase name preserveAcronyms=true) (compatClusterNameRemapping name))}}
@implementation MTRCluster{{compatClusterNameRemapping name}}
@end
{{/unless}}
@implementation MTRCluster{{compatClusterNameRemapping name}} (Deprecated)
- (instancetype)initWithDevice:(MTRDevice *)device endpoint:(uint16_t)endpoint queue:(dispatch_queue_t)queue
{
return [self initWithDevice:device endpointID:@(endpoint) queue:queue];
}
{{#chip_cluster_commands}}
{{! Takes two arguments: cluster name and command name, plus the ambient state where the command is "this" }}
{{#*inline "commandImpl"}}
{{#unless (wasRemoved cluster command=command)}}
- (void){{asLowerCamelCase command}}WithParams:(MTR{{cluster}}Cluster{{command}}Params * {{#unless (commandHasRequiredField .)}}_Nullable{{/unless}})params expectedValues:(NSArray<NSDictionary<NSString *, id> *> * _Nullable)expectedDataValueDictionaries expectedValueInterval:(NSNumber * _Nullable)expectedValueIntervalMs completionHandler:({{>command_completion_type command=. compatRemapNames=true}})completionHandler
{
[self {{asLowerCamelCase name}}WithParams:params expectedValues:expectedDataValueDictionaries expectedValueInterval:expectedValueIntervalMs completion:
{{#if hasSpecificResponse}}
^(MTR{{asUpperCamelCase parent.name preserveAcronyms=true}}Cluster{{asUpperCamelCase responseName preserveAcronyms=true}}Params * _Nullable data, NSError * _Nullable error) {
// Cast is safe because subclass does not add any selectors.
completionHandler(static_cast<MTR{{cluster}}Cluster{{compatCommandNameRemapping parent.name responseName}}Params *>(data), error);
}
{{else}}
completionHandler
{{/if}}
];
}
{{#unless (hasArguments)}}
- (void){{asLowerCamelCase command}}WithExpectedValues:(NSArray<NSDictionary<NSString *, id> *> *)expectedValues expectedValueInterval:(NSNumber *)expectedValueIntervalMs completionHandler:({{>command_completion_type command=. compatRemapNames=true}})completionHandler
{
[self {{asLowerCamelCase name}}WithParams:nil expectedValues:expectedValues expectedValueInterval:expectedValueIntervalMs completionHandler:completionHandler];
}
{{/unless}}
{{/unless}}
{{/inline}}
{{> commandImpl cluster=(compatClusterNameRemapping parent.name)
command=(compatCommandNameRemapping parent.name name)}}
{{/chip_cluster_commands}}
{{~#chip_server_cluster_attributes}}
{{~#*inline "attributeImpls"}}
{{#unless (isStrEqual attribute (asUpperCamelCase name preserveAcronyms=true))}}
- (NSDictionary<NSString *, id> *)readAttribute{{attribute}}WithParams:(MTRReadParams * _Nullable)params
{
return [self readAttribute{{asUpperCamelCase name preserveAcronyms=true}}WithParams:params];
}
{{#if isWritableAttribute}}
- (void)writeAttribute{{attribute}}WithValue:(NSDictionary<NSString *, id> *)dataValueDictionary expectedValueInterval:(NSNumber *)expectedValueIntervalMs
{
[self writeAttribute{{asUpperCamelCase name preserveAcronyms=true}}WithValue:dataValueDictionary expectedValueInterval:expectedValueIntervalMs];
}
- (void)writeAttribute{{attribute}}WithValue:(NSDictionary<NSString *, id> *)dataValueDictionary expectedValueInterval:(NSNumber *)expectedValueIntervalMs params:(MTRWriteParams * _Nullable)params
{
[self writeAttribute{{asUpperCamelCase name preserveAcronyms=true}}WithValue:dataValueDictionary expectedValueInterval:expectedValueIntervalMs params:params];
}
{{/if}}
{{/unless}}
{{/inline~}}
{{> attributeImpls attribute=(compatAttributeNameRemapping parent.name name)}}
{{/chip_server_cluster_attributes}}
@end
{{/chip_client_clusters}}
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)