Skip to content

Commit

Permalink
Fix Darwin APIs for opening commissioning windows per API review. (#2…
Browse files Browse the repository at this point in the history
…2521)

Fixes #18527

Addresses part of #22420
  • Loading branch information
bzbarsky-apple authored Sep 12, 2022
1 parent 8af13a2 commit b89aa66
Show file tree
Hide file tree
Showing 17 changed files with 754 additions and 375 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,57 @@

#import <Matter/Matter.h>

#import "MTRError_Utils.h"

#include "OpenCommissioningWindowCommand.h"

CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
{
mWorkQueue = dispatch_queue_create("com.chip.open_commissioning_window", DISPATCH_QUEUE_SERIAL);
auto * controller = CurrentCommissioner();
NSError * error;
__block NSString * pairingCode;
auto * device = [[MTRBaseDevice alloc] initWithNodeID:@(mNodeId) controller:controller];

auto * self = this;
if (mCommissioningWindowOption == 0) {
[controller openPairingWindow:mNodeId duration:mCommissioningWindowTimeoutMs error:&error];
auto * cluster = [[MTRBaseClusterAdministratorCommissioning alloc] initWithDevice:device endpoint:0 queue:mWorkQueue];
auto * params = [[MTRAdministratorCommissioningClusterOpenBasicCommissioningWindowParams alloc] init];
params.commissioningTimeout = @(mCommissioningWindowTimeoutMs);
params.timedInvokeTimeoutMs = @(10000);
[cluster openBasicCommissioningWindowWithParams:params
completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
self->SetCommandExitStatus(CHIP_NO_ERROR);
} else {
self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error));
}
}];
} else {
pairingCode = [controller openPairingWindowWithPIN:mNodeId
duration:mCommissioningWindowTimeoutMs
discriminator:mDiscriminator
setupPIN:[MTRSetupPayload generateRandomPIN]
error:&error];
}
[device
openCommissioningWindowWithSetupPasscode:[MTRSetupPayload generateRandomSetupPasscode]
discriminator:@(mDiscriminator)
duration:@(mCommissioningWindowTimeoutMs)
clientQueue:mWorkQueue
completion:^(MTRSetupPayload * _Nullable payload, NSError * error) {
if (error != nil) {
self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error));
return;
}

if (error != nil) {
SetCommandExitStatus(error);
return CHIP_NO_ERROR;
}
if (payload == nil) {
self->SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
return;
}

// TODO: Those should be async operations and we should not claim to
// be done until they complete. As things stand, we have no idea
// how to tell when we're done, so just set a timer for slightly
// less than our command timeout to call SetCommandExitStatus.
mWorkQueue = dispatch_queue_create("com.chip.open_commissioning_window", DISPATCH_QUEUE_SERIAL);
mTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mWorkQueue);
auto * self = this;
dispatch_source_set_event_handler(mTimer, ^{
dispatch_source_cancel(mTimer);
mTimer = nil;
mWorkQueue = nil;
if (pairingCode != nil) {
ChipLogProgress(chipTool, "Setup code: %s\n", [pairingCode UTF8String]);
}
self->SetCommandExitStatus(CHIP_NO_ERROR);
});
dispatch_source_set_timer(
mTimer, dispatch_time(DISPATCH_TIME_NOW, (GetWaitDuration().count() - 2000) * NSEC_PER_MSEC), DISPATCH_TIME_FOREVER, 0);
dispatch_resume(mTimer);
auto * pairingCode = [payload manualEntryCode];
if (pairingCode == nil) {
self->SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
return;
}

ChipLogProgress(chipTool, "Setup code: %s\n", [[payload manualEntryCode] UTF8String]);
self->SetCommandExitStatus(CHIP_NO_ERROR);
}];
}

return CHIP_NO_ERROR;
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ - (IBAction)overrideControls:(id)sender

- (IBAction)openPairingWindow:(id)sender
{
NSUInteger setupPIN = [MTRSetupPayload generateRandomPIN];
[_deviceSelector forSelectedDevices:^(uint64_t deviceId) {
if (MTRGetConnectedDeviceWithID(deviceId, ^(MTRBaseDevice * _Nullable chipDevice, NSError * _Nullable error) {
if (chipDevice) {
Expand All @@ -209,36 +208,60 @@ - (IBAction)openPairingWindow:(id)sender
}
int timeout = [timeoutStr intValue];

NSString * output;
NSError * error;
MTRDeviceController * controller = InitializeMTR();
if ([self.useOnboardingTokenSwitch isOn]) {
NSString * discriminatorStr = [self.discriminatorField text];
if (discriminatorStr.length == 0) {
discriminatorStr = [self.discriminatorField placeholder];
}
NSInteger discriminator = [discriminatorStr intValue];

output = [controller openPairingWindowWithPIN:deviceId
duration:timeout
discriminator:discriminator
setupPIN:setupPIN
error:&error];

if (output != nil) {
NSString * result = [@"Use Manual Code: " stringByAppendingString:output];
[self updateResult:result];
} else {
[self updateResult:@"Failed in opening the pairing window"];
}
__auto_type setupPasscode = [MTRSetupPayload generateRandomSetupPasscode];
[chipDevice
openCommissioningWindowWithSetupPasscode:setupPasscode
discriminator:@(discriminator)
duration:@(timeout)
clientQueue:dispatch_get_main_queue()
completion:^(
MTRSetupPayload * _Nullable payload, NSError * _Nullable error) {
NSString * _Nullable code = nil;
if (payload != nil) {
code = [payload manualEntryCode];
}

if (code != nil) {
NSString * result =
[@"Use Manual Code: " stringByAppendingString:code];
[self updateResult:result];
} else {
NSString * errorDesc;
if (error == nil) {
errorDesc = @"Failed to generate manual code";
} else {
errorDesc = error.localizedDescription;
}
[self updateResult:[@"Failed in opening the pairing window"
stringByAppendingString:errorDesc]];
}
}];
} else {
BOOL didSend = [controller openPairingWindow:deviceId duration:timeout error:&error];
if (didSend) {
[self updateResult:@"Scan the QR code on the device"];
} else {
NSString * errorString = [@"Error: " stringByAppendingString:error.localizedDescription];
[self updateResult:errorString];
}
__auto_type * cluster =
[[MTRBaseClusterAdministratorCommissioning alloc] initWithDevice:chipDevice
endpoint:0
queue:dispatch_get_main_queue()];
__auto_type * params =
[[MTRAdministratorCommissioningClusterOpenBasicCommissioningWindowParams alloc] init];
params.commissioningTimeout = @(timeout);
params.timedInvokeTimeoutMs = @(10000);
[cluster openBasicCommissioningWindowWithParams:params
completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
[self updateResult:@"Scan the QR code on the device"];
} else {
NSString * errorString =
[@"Error: " stringByAppendingString:error.localizedDescription];
[self updateResult:errorString];
}
}];
}
} else {
[self updateResult:[NSString stringWithFormat:@"Failed to establish a connection with the device"]];
Expand Down
36 changes: 36 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#import <Foundation/Foundation.h>

@class MTRSetupPayload;

NS_ASSUME_NONNULL_BEGIN

/**
Expand Down Expand Up @@ -87,6 +89,11 @@ typedef void (^MTRDeviceErrorHandler)(NSError * error);
*/
typedef void (^MTRDeviceResubscriptionScheduledHandler)(NSError * error, NSNumber * resubscriptionDelay);

/**
* Handler for openCommissioningWindow.
*/
typedef void (^MTRDeviceOpenCommissioningWindowHandler)(MTRSetupPayload * _Nullable payload, NSError * _Nullable error);

extern NSString * const MTRAttributePathKey;
extern NSString * const MTRCommandPathKey;
extern NSString * const MTREventPathKey;
Expand All @@ -109,12 +116,21 @@ extern NSString * const MTRArrayValueType;
@class MTRAttributeCacheContainer;
@class MTRReadParams;
@class MTRSubscribeParams;
@class MTRDeviceController;

@interface MTRBaseDevice : NSObject

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

/**
* Initialize the device object with the given node id and controller. This
* will always succeed, even if there is no such node id on the controller's
* fabric, but attempts to actually use the MTRBaseDevice will fail
* (asynchronously) in that case.
*/
- (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller;

/**
* Subscribe to receive attribute reports for everything (all endpoints, all
* clusters, all attributes, all events) on the device.
Expand Down Expand Up @@ -238,6 +254,26 @@ extern NSString * const MTRArrayValueType;
*/
- (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion;

/**
* Open a commissioning window on the device.
*
* On success, completion will be called with the MTRSetupPayload that
* can be used to commission the device.
*
* @param setupPasscode The setup passcode to use for the commissioning window.
* See MTRSetupPayload's generateRandomSetupPasscode for
* generating a valid random passcode.
* @param discriminator The discriminator to use for the commissionable
* advertisement.
* @param duration Duration, in seconds, during which the commissioning
* window will be open.
*/
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
discriminator:(NSNumber *)discriminator
duration:(NSNumber *)duration
clientQueue:(dispatch_queue_t)clientQueue
completion:(MTRDeviceOpenCommissioningWindowHandler)completion;

@end

@interface MTRAttributePath : NSObject <NSCopying>
Expand Down
Loading

0 comments on commit b89aa66

Please sign in to comment.