blob: a8a4c9deee6540961aa117971a41154be2c32380 [file] [log] [blame]
{{> header excludeZapComment=true}}
#import <Foundation/Foundation.h>
#import "MTRAttributeCacheContainer_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"
#include <lib/support/CHIPListUtils.h>
#include <platform/CHIPDeviceLayer.h>
#include <type_traits>
#include <controller/TypedReadCallback.h>
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) {
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}}
chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint);
return cppCluster.InvokeCommand(request, bridge, successCb, failureCb, 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];
{{/if_is_fabric_scoped_struct~}}
auto * bridge = new MTR{{>attribute_data_callback_name}}CallbackBridge(self.callbackQueue,
{{! This treats completion as taking an id for the data. This is
not great from a type-safety perspective, of course. }}
completion,
^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>attribute_data_callback_name}}Callback successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
using TypeInfo = {{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo;
chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint);
return cppCluster.ReadAttribute<TypeInfo>(bridge, successCb, failureCb
{{~#if_is_fabric_scoped_struct type~}}
, params.filterByFabric
{{~/if_is_fabric_scoped_struct~}}
);
});
std::move(*bridge).DispatchAction(self.device);
}
{{#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
{
// Make a copy of params before we go async.
params = [params copy];
auto * bridge = new MTR{{>attribute_data_callback_name}}CallbackSubscriptionBridge(self.callbackQueue,
{{! This treats reportHandler as taking an id for the data. This is
not great from a type-safety perspective, of course. }}
reportHandler,
^(ExchangeManager & exchangeManager, const SessionHandle & session, {{>attribute_data_callback_name}}Callback successCb, MTRErrorCallback failureCb, MTRCallbackBridgeBase * bridge) {
auto * typedBridge = static_cast<MTR{{>attribute_data_callback_name}}CallbackSubscriptionBridge *>(bridge);
if (!params.resubscribeIfLost) {
// We don't support disabling auto-resubscribe.
return CHIP_ERROR_INVALID_ARGUMENT;
}
using TypeInfo = {{asUpperCamelCase parent.name}}::Attributes::{{asUpperCamelCase name}}::TypeInfo;
chip::Controller::{{asUpperCamelCase parent.name}}Cluster cppCluster(exchangeManager, session, self->_endpoint);
CHIP_ERROR err = cppCluster.SubscribeAttribute<TypeInfo>(bridge, successCb, failureCb, [params.minInterval unsignedShortValue], [params.maxInterval unsignedShortValue], MTR{{>attribute_data_callback_name}}CallbackSubscriptionBridge::OnSubscriptionEstablished, nil,
params.filterByFabric,
!params.replaceExistingSubscriptions,
chip::NullOptional,
[typedBridge](void) { typedBridge->OnDone(); }
);
if (err == CHIP_NO_ERROR) {
// Now that we kicked off the subscribe, flag our callback bridge
// to stay alive until we get an OnDone.
typedBridge->KeepAliveOnCallback();
}
return err;
}, subscriptionEstablished);
std::move(*bridge).DispatchAction(self.device);
}
+ (void) read{{>attribute}}WithAttributeCache:(MTRAttributeCacheContainer *)attributeCacheContainer 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 (attributeCacheContainer.cppAttributeCache) {
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 = attributeCacheContainer.cppAttributeCache->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}}WithAttributeCache:attributeCacheContainer 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)