| {{> header excludeZapComment=true}} |
| |
| #import <Foundation/Foundation.h> |
| |
| #import "MTRClusterStateCacheContainer_Internal.h" |
| #import "MTRBaseClusters_internal.h" |
| #import "MTRBaseDevice.h" |
| #import "MTRBaseDevice_Internal.h" |
| #import "MTRCallbackBridge_internal.h" |
| #import "MTRCluster_internal.h" |
| #import "MTRStructsObjc.h" |
| #import "MTRCommandPayloadsObjc.h" |
| #import "MTRBaseClustersCpp_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; |
| |
| // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks): Linter is unable to locate the delete on these objects. |
| {{#chip_client_clusters includeAll=true}} |
| @implementation MTRBaseCluster{{asUpperCamelCase name}} |
| |
| - (instancetype)initWithDevice:(MTRBaseDevice *)device endpointID:(NSNumber *)endpointID queue:(dispatch_queue_t)queue |
| { |
| if (self = [super initWithQueue:queue]) { |
| if (device == nil) { |
| return nil; |
| } |
| |
| _device = device; |
| {{!TODO consider range-checking the incoming number to make sure it's |
| actually in the uint16_t range}} |
| _endpoint = [endpointID unsignedShortValue]; |
| } |
| return self; |
| } |
| |
| {{#chip_cluster_commands}} |
| {{#*inline "callbackName"}}{{#if hasSpecificResponse}}{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase responseName}}{{else}}CommandSuccess{{/if}}{{/inline}} |
| {{#unless (hasArguments)}} |
| - (void){{asLowerCamelCase name}}WithCompletion:({{>command_completion_type command=.}})completion |
| { |
| [self {{asLowerCamelCase name}}WithParams:nil completion:completion]; |
| } |
| {{/unless}} |
| - (void){{asLowerCamelCase name}}WithParams: (MTR{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase name}}Params * {{#unless (commandHasRequiredField .)}}_Nullable{{/unless}})params completion:({{>command_completion_type command=.}})completion |
| { |
| // Make a copy of params before we go async. |
| params = [params copy]; |
| auto * bridge = new MTR{{>callbackName}}CallbackBridge(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, |
| {{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. }} |
| ^(id _Nullable value, NSError * _Nullable error) { |
| completion(error); |
| }, |
| {{/if}} |
| ^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>callbackName}}CallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) { |
| auto * typedBridge = static_cast<MTR{{>callbackName}}CallbackBridge *>(bridge); |
| chip::Optional<uint16_t> timedInvokeTimeoutMs; |
| ListFreer listFreer; |
| {{asUpperCamelCase parent.name}}::Commands::{{asUpperCamelCase name}}::Type request; |
| if (params != nil) { |
| if (params.timedInvokeTimeoutMs != nil) { |
| timedInvokeTimeoutMs.SetValue(params.timedInvokeTimeoutMs.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}} |
| |
| return MTRStartInvokeInteraction(typedBridge, request, exchangeManager, session, successCb, failureCb, self->_endpoint, timedInvokeTimeoutMs); |
| }); |
| std::move(*bridge).DispatchAction(self.device); |
| } |
| {{/chip_cluster_commands}} |
| |
| {{#chip_server_cluster_attributes}} |
| {{#*inline "attribute"}}Attribute{{asUpperCamelCase name}}{{/inline}} |
| - (void)read{{>attribute}}With |
| {{~#if_is_fabric_scoped_struct type~}} |
| Params:(MTRReadParams * _Nullable)params completion: |
| {{~else~}} |
| Completion: |
| {{~/if_is_fabric_scoped_struct~}} |
| (void (^)({{asObjectiveCClass type parent.name}} * _Nullable value, NSError * _Nullable error))completion |
| { |
| {{~#if_is_fabric_scoped_struct type}} |
| // Make a copy of params before we go async. |
| params = [params copy]; |
| {{else}} |
| MTRReadParams * params = [[MTRReadParams alloc] init]; |
| {{/if_is_fabric_scoped_struct~}} |
| using TypeInfo = {{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo; |
| return MTRReadAttribute<MTR{{>attribute_data_callback_name}}CallbackBridge, |
| {{asObjectiveCClass type parent.name}}, |
| TypeInfo::DecodableType>(params, completion, self.callbackQueue, self.device, self->_endpoint, TypeInfo::GetClusterId(), TypeInfo::GetAttributeId()); |
| } |
| |
| {{#if isWritableAttribute}} |
| {{#*inline "callbackName"}}DefaultSuccess{{/inline}} |
| - (void)write{{>attribute}}WithValue:({{asObjectiveCType type parent.name}})value completion:(MTRStatusCompletion)completion |
| { |
| [self write{{>attribute}}WithValue:({{asObjectiveCType type parent.name}})value params:nil completion:completion]; |
| } |
| - (void)write{{>attribute}}WithValue:({{asObjectiveCType type parent.name}})value params:(MTRWriteParams * _Nullable)params completion:(MTRStatusCompletion)completion |
| { |
| // Make a copy of params before we go async. |
| params = [params copy]; |
| value = [value copy]; |
| |
| auto * bridge = new MTR{{>callbackName}}CallbackBridge(self.callbackQueue, |
| {{! 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. }} |
| ^(id _Nullable ignored, NSError * _Nullable error) { |
| completion(error); |
| }, |
| ^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>callbackName}}CallbackType successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) { |
| chip::Optional<uint16_t> timedWriteTimeout; |
| if (params != nil) { |
| if (params.timedWriteTimeout != nil){ |
| timedWriteTimeout.SetValue(params.timedWriteTimeout.unsignedShortValue); |
| } |
| } |
| {{#if mustUseTimedInvoke}} |
| if (!timedWriteTimeout.HasValue()) { |
| timedWriteTimeout.SetValue(10000); |
| } |
| {{/if}} |
| |
| ListFreer listFreer; |
| using TypeInfo = {{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo; |
| TypeInfo::Type cppValue; |
| {{>encode_value target="cppValue" source="value" cluster=parent.name errorCode="return CHIP_ERROR_INVALID_ARGUMENT;" depth=0}} |
| |
| chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint); |
| return cppCluster.WriteAttribute<TypeInfo>(cppValue, bridge, successCb, failureCb, timedWriteTimeout); |
| }); |
| std::move(*bridge).DispatchAction(self.device); |
| } |
| |
| {{/if}} |
| {{#if isReportableAttribute}} |
| - (void) subscribe{{>attribute}}WithParams:(MTRSubscribeParams * _Nonnull)params |
| subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished |
| reportHandler:(void (^)({{asObjectiveCClass type parent.name}} * _Nullable value, NSError * _Nullable error))reportHandler |
| { |
| using TypeInfo = {{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo; |
| MTRSubscribeAttribute<MTR{{>attribute_data_callback_name}}CallbackSubscriptionBridge, {{asObjectiveCClass type parent.name}}, TypeInfo::DecodableType>(params, subscriptionEstablished, reportHandler, self.callbackQueue, self.device, self->_endpoint, TypeInfo::GetClusterId(), TypeInfo::GetAttributeId()); |
| } |
| |
| + (void) read{{>attribute}}WithClusterStateCache:(MTRClusterStateCacheContainer *)clusterStateCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completion:(void (^)({{asObjectiveCClass type parent.name}} * _Nullable value, NSError * _Nullable error))completion |
| { |
| auto * bridge = new MTR{{>attribute_data_callback_name}}CallbackBridge(queue, completion); |
| std::move(*bridge).DispatchLocalAction(^({{>attribute_data_callback_name}}Callback successCb, MTRErrorCallback failureCb) { |
| if (clusterStateCacheContainer.cppClusterStateCache) { |
| chip::app::ConcreteAttributePath path; |
| using TypeInfo = {{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo; |
| path.mEndpointId = static_cast<chip::EndpointId>([endpoint unsignedShortValue]); |
| path.mClusterId = TypeInfo::GetClusterId(); |
| path.mAttributeId = TypeInfo::GetAttributeId(); |
| TypeInfo::DecodableType value; |
| CHIP_ERROR err = clusterStateCacheContainer.cppClusterStateCache->Get<TypeInfo>(path, value); |
| if (err == CHIP_NO_ERROR) |
| { |
| successCb(bridge, value); |
| } |
| return err; |
| } |
| return CHIP_ERROR_NOT_FOUND; |
| }); |
| } |
| |
| {{/if}} |
| {{/chip_server_cluster_attributes}} |
| |
| @end |
| {{#if (isStrEqual (asUpperCamelCase name) "UnitTesting")}} |
| |
| @implementation MTRBaseClusterTestCluster |
| @end |
| {{/if}} |
| |
| @implementation MTRBaseCluster{{compatClusterNameRemapping name}} (Deprecated) |
| |
| {{#chip_cluster_commands}} |
| - (void){{asLowerCamelCase name}}WithParams:(MTR{{compatClusterNameRemapping parent.name}}Cluster{{asUpperCamelCase name}}Params * {{#unless (commandHasRequiredField .)}}_Nullable{{/unless}})params completionHandler:({{>command_completion_type command=. compatRemapClusterName=true}})completionHandler |
| { |
| [self {{asLowerCamelCase name}}WithParams:params completion: |
| {{#if hasSpecificResponse}} |
| ^(MTR{{asUpperCamelCase parent.name}}Cluster{{asUpperCamelCase responseName}}Params * _Nullable data, NSError * _Nullable error) { |
| // Cast is safe because subclass does not add any selectors. |
| completionHandler(static_cast<MTR{{compatClusterNameRemapping parent.name}}Cluster{{asUpperCamelCase responseName}}Params *>(data), error); |
| } |
| {{else}} |
| completionHandler |
| {{/if}} |
| ]; |
| } |
| {{#unless (hasArguments)}} |
| - (void){{asLowerCamelCase name}}WithCompletionHandler:({{>command_completion_type command=. compatRemapClusterName=true}})completionHandler |
| { |
| [self {{asLowerCamelCase name}}WithParams:nil completionHandler:completionHandler]; |
| } |
| {{/unless}} |
| {{/chip_cluster_commands}} |
| |
| {{#chip_server_cluster_attributes}} |
| {{!Backwards compat for now: Treat DeviceTypeList as DeviceList. Ideally we would have both, not just DeviceList. }} |
| {{#*inline "attribute"}}Attribute{{#if (isStrEqual (asUpperCamelCase parent.name) "Descriptor")}}{{#if (isStrEqual (asUpperCamelCase name) "DeviceTypeList")}}DeviceList{{else}}{{asUpperCamelCase name}}{{/if}}{{else}}{{asUpperCamelCase name}}{{/if}}{{/inline}} |
| {{! TODO: We need a better setup for the API_AVALABLE annotations here; this does not scale at all sanely. }} |
| - (void)read{{>attribute}}With |
| {{~#if_is_fabric_scoped_struct type~}} |
| Params:(MTRReadParams * _Nullable)params completionHandler: |
| {{~else~}} |
| CompletionHandler: |
| {{~/if_is_fabric_scoped_struct~}} |
| (void (^)({{asObjectiveCClass type parent.name compatRemapClusterName=true}} * _Nullable value, NSError * _Nullable error))completionHandler |
| { |
| [self readAttribute{{asUpperCamelCase name}}With{{#if_is_fabric_scoped_struct type}}Params:params completion:{{else}}Completion:{{/if_is_fabric_scoped_struct}} |
| ^({{asObjectiveCClass type parent.name}} * _Nullable value, NSError * _Nullable error) { |
| // Cast is safe because subclass does not add any selectors. |
| completionHandler(static_cast<{{asObjectiveCClass type parent.name compatRemapClusterName=true}} *>(value), error); |
| }]; |
| } |
| {{#if isWritableAttribute}} |
| - (void)write{{>attribute}}WithValue:({{asObjectiveCType type parent.name compatRemapClusterName=true}})value completionHandler:(MTRStatusCompletion)completionHandler |
| { |
| [self writeAttribute{{asUpperCamelCase name}}WithValue:value params:nil completion:completionHandler]; |
| } |
| - (void)write{{>attribute}}WithValue:({{asObjectiveCType type parent.name compatRemapClusterName=true}})value params:(MTRWriteParams * _Nullable)params completionHandler:(MTRStatusCompletion)completionHandler |
| { |
| [self writeAttribute{{asUpperCamelCase name}}WithValue:value params:params completion:completionHandler]; |
| } |
| {{/if}} |
| {{#if isReportableAttribute}} |
| - (void) subscribe{{>attribute}}WithMinInterval:(NSNumber * _Nonnull)minInterval maxInterval:(NSNumber * _Nonnull)maxInterval |
| params:(MTRSubscribeParams * _Nullable)params |
| subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablishedHandler reportHandler:(void (^)({{asObjectiveCClass type parent.name compatRemapClusterName=true}} * _Nullable value, NSError * _Nullable error))reportHandler |
| { |
| MTRSubscribeParams * _Nullable subscribeParams = [params copy]; |
| if (subscribeParams == nil) { |
| subscribeParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval maxInterval:maxInterval]; |
| } else { |
| subscribeParams.minInterval = minInterval; |
| subscribeParams.maxInterval = maxInterval; |
| } |
| [self subscribeAttribute{{asUpperCamelCase name}}WithParams:subscribeParams subscriptionEstablished:subscriptionEstablishedHandler reportHandler: |
| ^({{asObjectiveCClass type parent.name}} * _Nullable value, NSError * _Nullable error) { |
| // Cast is safe because subclass does not add any selectors. |
| reportHandler(static_cast<{{asObjectiveCClass type parent.name compatRemapClusterName=true}} *>(value), error) ; |
| }]; |
| } |
| + (void) read{{>attribute}}WithAttributeCache:(MTRAttributeCacheContainer *)attributeCacheContainer endpoint:(NSNumber *)endpoint queue:(dispatch_queue_t)queue completionHandler:(void (^)({{asObjectiveCClass type parent.name compatRemapClusterName=true}} * _Nullable value, NSError * _Nullable error))completionHandler |
| { |
| [self readAttribute{{asUpperCamelCase name}}WithClusterStateCache:attributeCacheContainer.realContainer endpoint:endpoint queue:queue completion: |
| ^({{asObjectiveCClass type parent.name}} * _Nullable value, NSError * _Nullable error) { |
| // Cast is safe because subclass does not add any selectors. |
| completionHandler(static_cast<{{asObjectiveCClass type parent.name compatRemapClusterName=true}} *>(value), error); |
| }]; |
| } |
| {{/if}} |
| {{/chip_server_cluster_attributes}} |
| |
| - (nullable instancetype)initWithDevice:(MTRBaseDevice *)device |
| endpoint:(uint16_t)endpoint |
| queue:(dispatch_queue_t)queue |
| { |
| return [self initWithDevice:device endpointID:@(endpoint) queue:queue]; |
| } |
| |
| @end |
| |
| {{/chip_client_clusters}} |
| |
| // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) |