| {{> 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) |