3 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #import "QRCodeViewController.h"
21 #import "CHIPUIViewUtils.h"
22 #import "DefaultsUtils.h"
23 #import "DeviceSelector.h"
27 #import <AVFoundation/AVFoundation.h>
29 #define INDICATOR_DELAY 0.5 * NSEC_PER_SEC
30 #define ERROR_DISPLAY_TIME 2.0 * NSEC_PER_SEC
31 #define QR_CODE_FREEZE 1.0 * NSEC_PER_SEC
33 // The expected Vendor ID for CHIP demos
34 // 0x235A: Chip's Vendor Id
35 #define EXAMPLE_VENDOR_ID 0x235A
37 #define EXAMPLE_VENDOR_TAG_IP 1
40 #define NETWORK_CHIP_PREFIX @"CHIP-"
42 #define NOT_APPLICABLE_STRING @"N/A"
44 @interface QRCodeViewController ()
46 @property (nonatomic, strong) AVCaptureSession * captureSession;
47 @property (nonatomic, strong) AVCaptureVideoPreviewLayer * videoPreviewLayer;
49 @property (strong, nonatomic) UIView * qrCodeViewPreview;
51 @property (strong, nonatomic) UITextField * manualCodeTextField;
52 @property (strong, nonatomic) UIButton * doneManualCodeButton;
54 @property (strong, nonatomic) UIButton * nfcScanButton;
55 @property (readwrite) BOOL sessionIsActive;
57 @property (strong, nonatomic) UIView * setupPayloadView;
58 @property (strong, nonatomic) UILabel * manualCodeLabel;
59 @property (strong, nonatomic) UIButton * resetButton;
60 @property (strong, nonatomic) UILabel * versionLabel;
61 @property (strong, nonatomic) UILabel * discriminatorLabel;
62 @property (strong, nonatomic) UILabel * setupPinCodeLabel;
63 @property (strong, nonatomic) UILabel * rendezVousInformation;
64 @property (strong, nonatomic) UILabel * vendorID;
65 @property (strong, nonatomic) UILabel * productID;
66 @property (strong, nonatomic) UILabel * serialNumber;
68 @property (strong, nonatomic) UIActivityIndicatorView * activityIndicator;
69 @property (strong, nonatomic) UILabel * errorLabel;
71 @property (readwrite) CHIPDeviceController * chipController;
73 @property (strong, nonatomic) NFCNDEFReaderSession * session;
74 @property (strong, nonatomic) CHIPSetupPayload * setupPayload;
75 @property (strong, nonatomic) DeviceSelector * deviceList;
78 @implementation QRCodeViewController {
79 dispatch_queue_t _captureSessionQueue;
84 - (void)changeNavBarButtonToCamera
86 UIBarButtonItem * camera = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
88 action:@selector(startScanningQRCode:)];
89 self.navigationItem.rightBarButtonItem = camera;
92 - (void)changeNavBarButtonToCancel
94 UIBarButtonItem * cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
96 action:@selector(stopScanningQRCode:)];
97 self.navigationItem.rightBarButtonItem = cancel;
102 self.view.backgroundColor = UIColor.whiteColor;
104 // Setup nav bar button
105 [self changeNavBarButtonToCamera];
108 UILabel * titleLabel = [CHIPUIViewUtils addTitle:@"QR Code Parser" toView:self.view];
111 UIStackView * stackView = [UIStackView new];
112 stackView.axis = UILayoutConstraintAxisVertical;
113 stackView.distribution = UIStackViewDistributionFill;
114 stackView.alignment = UIStackViewAlignmentLeading;
115 stackView.spacing = 15;
116 [self.view addSubview:stackView];
118 stackView.translatesAutoresizingMaskIntoConstraints = false;
119 [stackView.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:30].active = YES;
120 [stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:30].active = YES;
121 [stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-30].active = YES;
124 _manualCodeTextField = [UITextField new];
125 _doneManualCodeButton = [UIButton new];
126 [_doneManualCodeButton addTarget:self action:@selector(enteredManualCode:) forControlEvents:UIControlEventTouchUpInside];
127 _manualCodeTextField.placeholder = @"Manual Code";
128 _manualCodeTextField.keyboardType = UIKeyboardTypeNumberPad;
129 [_doneManualCodeButton setTitle:@"Go" forState:UIControlStateNormal];
130 UIView * manualEntryView = [CHIPUIViewUtils viewWithUITextField:_manualCodeTextField button:_doneManualCodeButton];
131 [stackView addArrangedSubview:manualEntryView];
133 manualEntryView.translatesAutoresizingMaskIntoConstraints = false;
134 [manualEntryView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:30].active = YES;
135 [manualEntryView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-30].active = YES;
136 [manualEntryView.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:30].active = YES;
138 _nfcScanButton = [UIButton new];
139 [_nfcScanButton setTitle:@"Scan NFC Tag" forState:UIControlStateNormal];
140 [_nfcScanButton addTarget:self action:@selector(startScanningNFCTags:) forControlEvents:UIControlEventTouchDown];
141 _nfcScanButton.titleLabel.font = [UIFont systemFontOfSize:17];
142 _nfcScanButton.titleLabel.textColor = [UIColor blackColor];
143 _nfcScanButton.layer.cornerRadius = 5;
144 _nfcScanButton.clipsToBounds = YES;
145 _nfcScanButton.backgroundColor = UIColor.systemBlueColor;
146 [stackView addArrangedSubview:_nfcScanButton];
148 _nfcScanButton.translatesAutoresizingMaskIntoConstraints = false;
149 [_nfcScanButton.leadingAnchor constraintEqualToAnchor:stackView.leadingAnchor].active = YES;
150 [_nfcScanButton.trailingAnchor constraintEqualToAnchor:stackView.trailingAnchor].active = YES;
151 [_nfcScanButton.heightAnchor constraintEqualToConstant:40].active = YES;
153 _deviceList = [DeviceSelector new];
154 [_deviceList setEnabled:NO];
156 UILabel * deviceIDLabel = [UILabel new];
157 deviceIDLabel.text = @"Paired Devices:";
158 UIView * deviceIDView = [CHIPUIViewUtils viewWithLabel:deviceIDLabel textField:_deviceList];
159 [stackView addArrangedSubview:deviceIDView];
161 deviceIDView.translatesAutoresizingMaskIntoConstraints = false;
162 [deviceIDView.trailingAnchor constraintEqualToAnchor:stackView.trailingAnchor].active = true;
165 _setupPayloadView = [UIView new];
166 [self.view addSubview:_setupPayloadView];
168 _setupPayloadView.translatesAutoresizingMaskIntoConstraints = false;
169 [_setupPayloadView.topAnchor constraintEqualToAnchor:stackView.bottomAnchor constant:10].active = YES;
170 [_setupPayloadView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:30].active = YES;
171 [_setupPayloadView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-30].active = YES;
172 [_setupPayloadView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-30].active = YES;
174 [self addViewsToSetupPayloadView];
176 // activity indicator
177 _activityIndicator = [UIActivityIndicatorView new];
178 [self.view addSubview:_activityIndicator];
180 _activityIndicator.translatesAutoresizingMaskIntoConstraints = false;
181 [_activityIndicator.centerXAnchor constraintEqualToAnchor:_setupPayloadView.centerXAnchor].active = YES;
182 [_activityIndicator.centerYAnchor constraintEqualToAnchor:_setupPayloadView.centerYAnchor].active = YES;
183 _activityIndicator.color = UIColor.blackColor;
186 _qrCodeViewPreview = [UIView new];
187 [self.view addSubview:_qrCodeViewPreview];
189 _qrCodeViewPreview.translatesAutoresizingMaskIntoConstraints = false;
190 [_qrCodeViewPreview.topAnchor constraintEqualToAnchor:_nfcScanButton.bottomAnchor constant:30].active = YES;
191 [_qrCodeViewPreview.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:30].active = YES;
192 [_qrCodeViewPreview.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-30].active = YES;
193 [_qrCodeViewPreview.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-50].active = YES;
196 _errorLabel = [UILabel new];
197 _errorLabel.text = @"Error Text";
198 _errorLabel.textColor = UIColor.blackColor;
199 _errorLabel.font = [UIFont systemFontOfSize:17];
200 [stackView addArrangedSubview:_errorLabel];
202 _errorLabel.translatesAutoresizingMaskIntoConstraints = false;
203 [_errorLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:30].active = YES;
204 [_errorLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-30].active = YES;
207 _resetButton = [UIButton new];
208 [_resetButton setTitle:@"Reset" forState:UIControlStateNormal];
209 [_resetButton addTarget:self action:@selector(resetView:) forControlEvents:UIControlEventTouchUpInside];
210 _resetButton.backgroundColor = UIColor.systemBlueColor;
211 _resetButton.titleLabel.font = [UIFont systemFontOfSize:17];
212 _resetButton.titleLabel.textColor = [UIColor whiteColor];
213 _resetButton.layer.cornerRadius = 5;
214 _resetButton.clipsToBounds = YES;
215 [self.view addSubview:_resetButton];
217 _resetButton.translatesAutoresizingMaskIntoConstraints = false;
218 [_resetButton.widthAnchor constraintEqualToConstant:60].active = YES;
219 [_resetButton.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-30].active = YES;
220 [_resetButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-30].active = YES;
223 - (void)addViewsToSetupPayloadView
225 // manual entry field
226 _manualCodeLabel = [UILabel new];
227 _manualCodeLabel.text = @"00000000000000000000";
228 _manualCodeLabel.textColor = UIColor.systemBlueColor;
229 _manualCodeLabel.font = [UIFont systemFontOfSize:17];
230 _manualCodeLabel.textAlignment = NSTextAlignmentRight;
231 [_setupPayloadView addSubview:_manualCodeLabel];
233 _manualCodeLabel.translatesAutoresizingMaskIntoConstraints = false;
234 [_manualCodeLabel.topAnchor constraintEqualToAnchor:_setupPayloadView.topAnchor].active = YES;
235 [_manualCodeLabel.trailingAnchor constraintEqualToAnchor:_setupPayloadView.trailingAnchor].active = YES;
237 // Results scroll view
238 UIScrollView * resultsScrollView = [UIScrollView new];
239 [_setupPayloadView addSubview:resultsScrollView];
241 resultsScrollView.translatesAutoresizingMaskIntoConstraints = false;
242 [resultsScrollView.topAnchor constraintEqualToAnchor:_manualCodeLabel.bottomAnchor constant:10].active = YES;
243 [resultsScrollView.leadingAnchor constraintEqualToAnchor:_setupPayloadView.leadingAnchor].active = YES;
244 [resultsScrollView.trailingAnchor constraintEqualToAnchor:_setupPayloadView.trailingAnchor].active = YES;
245 [resultsScrollView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-20].active = YES;
247 UIStackView * parserResultsView = [UIStackView new];
248 parserResultsView.axis = UILayoutConstraintAxisVertical;
249 parserResultsView.distribution = UIStackViewDistributionEqualSpacing;
250 parserResultsView.alignment = UIStackViewAlignmentLeading;
251 parserResultsView.spacing = 15;
252 [resultsScrollView addSubview:parserResultsView];
254 parserResultsView.translatesAutoresizingMaskIntoConstraints = false;
255 [parserResultsView.topAnchor constraintEqualToAnchor:resultsScrollView.topAnchor].active = YES;
256 [parserResultsView.leadingAnchor constraintEqualToAnchor:resultsScrollView.leadingAnchor].active = YES;
257 [parserResultsView.trailingAnchor constraintEqualToAnchor:resultsScrollView.trailingAnchor].active = YES;
258 [parserResultsView.bottomAnchor constraintEqualToAnchor:resultsScrollView.bottomAnchor].active = YES;
259 [self addResultsUIToStackView:parserResultsView];
262 - (void)addResultsUIToStackView:(UIStackView *)stackView
264 NSArray<NSString *> * resultLabelTexts =
265 @[ @"version", @"discriminator", @"setup pin code", @"rendez vous information", @"vendor ID", @"product ID", @"serial #" ];
266 _versionLabel = [UILabel new];
267 _discriminatorLabel = [UILabel new];
268 _setupPinCodeLabel = [UILabel new];
269 _rendezVousInformation = [UILabel new];
270 _vendorID = [UILabel new];
271 _productID = [UILabel new];
272 _serialNumber = [UILabel new];
273 NSArray<UILabel *> * resultLabels =
274 @[ _versionLabel, _discriminatorLabel, _setupPinCodeLabel, _rendezVousInformation, _vendorID, _productID, _serialNumber ];
275 for (int i = 0; i < resultLabels.count && i < resultLabels.count; i++) {
276 UILabel * label = [UILabel new];
277 label.text = [resultLabelTexts objectAtIndex:i];
278 UILabel * result = [resultLabels objectAtIndex:i];
279 result.text = @"N/A";
280 UIStackView * labelStackView = [CHIPUIViewUtils stackViewWithLabel:label result:result];
281 labelStackView.translatesAutoresizingMaskIntoConstraints = false;
282 [stackView addArrangedSubview:labelStackView];
286 // MARK: UIViewController methods
288 - (void)viewDidDisappear:(BOOL)animated
290 [super viewDidDisappear:animated];
291 [_session invalidateSession];
295 - (void)dismissKeyboard
297 [_manualCodeTextField resignFirstResponder];
305 dispatch_queue_t callbackQueue = dispatch_queue_create("com.zigbee.chip.qrcodevc.callback", DISPATCH_QUEUE_SERIAL);
306 self.chipController = InitializeCHIP();
307 [self.chipController setPairingDelegate:self queue:callbackQueue];
309 UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
310 [self.view addGestureRecognizer:tap];
312 [self manualCodeInitialState];
313 [self qrCodeInitialState];
316 // MARK: NFCNDEFReaderSessionDelegate
318 - (void)readerSession:(nonnull NFCNDEFReaderSession *)session didDetectNDEFs:(nonnull NSArray<NFCNDEFMessage *> *)messages
320 [_session invalidateSession];
321 NSString * errorMessage;
322 if (messages.count == 1) {
323 for (NFCNDEFMessage * message in messages) {
324 if (message.records.count == 1) {
325 for (NFCNDEFPayload * payload in message.records) {
326 NSString * payloadType = [[NSString alloc] initWithData:payload.type encoding:NSUTF8StringEncoding];
327 if ([payloadType isEqualToString:@"U"]) {
328 NSURL * payloadURI = [payload wellKnownTypeURIPayload];
329 NSLog(@"Payload text:%@", payloadURI);
332 Once #415 goes in, there will b no need to replace _ with spaces.
334 NSString * qrCode = [[payloadURI absoluteString] stringByReplacingOccurrencesOfString:@"_"
336 NSLog(@"Scanned code string:%@", qrCode);
337 [self scannedQRCode:qrCode];
340 errorMessage = @"Record must be of type text.";
344 errorMessage = @"Only one record in NFC tag is accepted.";
348 errorMessage = @"Only one message in NFC tag is accepted.";
350 if ([errorMessage length] > 0) {
351 NSError * error = [[NSError alloc] initWithDomain:@"com.chiptool.nfctagscanning"
353 userInfo:@{ NSLocalizedDescriptionKey : errorMessage }];
354 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_NOW), dispatch_get_main_queue(), ^{
355 [self showError:error];
360 - (void)readerSession:(nonnull NFCNDEFReaderSession *)session didInvalidateWithError:(nonnull NSError *)error
362 NSLog(@"If no NFC reading UI is appearing, target may me missing the appropriate capability. Turn on Near Field Communication "
363 @"Tag Reading under the Capabilities tab for the project’s target. A paid developer account is needed for this.");
367 // MARK: CHIPDevicePairingDelegate
368 - (void)onNetworkCredentialsRequested:(CHIPNetworkCredentialType)type
370 NSLog(@"Network credential requested for pairing for type %lu", (unsigned long) type);
371 if (type == kNetworkCredentialTypeWiFi) {
372 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_NOW), dispatch_get_main_queue(), ^{
373 [self retrieveAndSendWifiCredentials];
376 NSLog(@"Unsupported credentials requested");
380 - (void)onPairingComplete:(NSError *)error
382 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_NOW), dispatch_get_main_queue(), ^{
383 [self->_deviceList refreshDeviceList];
387 // MARK: UI Helper methods
389 - (void)manualCodeInitialState
391 _setupPayloadView.hidden = YES;
392 _resetButton.hidden = YES;
393 _activityIndicator.hidden = YES;
394 _errorLabel.hidden = YES;
397 - (void)qrCodeInitialState
399 if ([_captureSession isRunning]) {
400 [_captureSession stopRunning];
402 if ([_activityIndicator isAnimating]) {
403 [_activityIndicator stopAnimating];
405 _resetButton.hidden = YES;
406 [self changeNavBarButtonToCamera];
407 _activityIndicator.hidden = YES;
408 _captureSession = nil;
409 [_videoPreviewLayer removeFromSuperlayer];
412 - (void)scanningStartState
414 [self changeNavBarButtonToCancel];
415 _setupPayloadView.hidden = YES;
416 _resetButton.hidden = YES;
417 _errorLabel.hidden = YES;
420 - (void)manualCodeEnteredStartState
422 self->_activityIndicator.hidden = NO;
423 [self->_activityIndicator startAnimating];
424 _setupPayloadView.hidden = YES;
425 _resetButton.hidden = YES;
426 _errorLabel.hidden = YES;
427 _manualCodeTextField.text = @"";
430 - (void)postScanningQRCodeState
432 _captureSession = nil;
433 [self changeNavBarButtonToCamera];
435 [_videoPreviewLayer removeFromSuperlayer];
437 self->_activityIndicator.hidden = NO;
438 [self->_activityIndicator startAnimating];
441 - (void)showError:(NSError *)error
443 [self->_activityIndicator stopAnimating];
444 self->_activityIndicator.hidden = YES;
445 self->_manualCodeLabel.hidden = YES;
446 _resetButton.hidden = YES;
448 self->_errorLabel.text = error.localizedDescription;
449 self->_errorLabel.hidden = NO;
450 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, ERROR_DISPLAY_TIME), dispatch_get_main_queue(), ^{
451 self->_errorLabel.hidden = YES;
455 - (void)showPayload:(CHIPSetupPayload *)payload decimalString:(nullable NSString *)decimalString
457 [self->_activityIndicator stopAnimating];
458 self->_activityIndicator.hidden = YES;
459 self->_errorLabel.hidden = YES;
460 // reset the view and remove any preferences that were stored from a previous scan
461 self->_setupPayloadView.hidden = NO;
462 self->_resetButton.hidden = NO;
464 [self updateUIFields:payload decimalString:decimalString];
465 [self parseOptionalData:payload];
466 [self handleRendezVous:payload];
469 - (void)retrieveAndSendWifiCredentials
471 UIAlertController * alertController =
472 [UIAlertController alertControllerWithTitle:@"Wifi Configuration"
473 message:@"Input network SSID and password that your phone is connected to."
474 preferredStyle:UIAlertControllerStyleAlert];
475 [alertController addTextFieldWithConfigurationHandler:^(UITextField * textField) {
476 textField.placeholder = @"Network SSID";
477 textField.clearButtonMode = UITextFieldViewModeWhileEditing;
478 textField.borderStyle = UITextBorderStyleRoundedRect;
480 NSString * networkSSID = CHIPGetDomainValueForKey(kCHIPToolDefaultsDomain, kNetworkSSIDDefaultsKey);
481 if ([networkSSID length] > 0) {
482 textField.text = networkSSID;
485 [alertController addTextFieldWithConfigurationHandler:^(UITextField * textField) {
486 [textField setSecureTextEntry:YES];
487 textField.placeholder = @"Password";
488 textField.clearButtonMode = UITextFieldViewModeWhileEditing;
489 textField.borderStyle = UITextBorderStyleRoundedRect;
490 textField.secureTextEntry = YES;
492 NSString * networkPassword = CHIPGetDomainValueForKey(kCHIPToolDefaultsDomain, kNetworkPasswordDefaultsKey);
493 if ([networkPassword length] > 0) {
494 textField.text = networkPassword;
497 [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
498 style:UIAlertActionStyleDefault
499 handler:^(UIAlertAction * action) {
502 __weak typeof(self) weakSelf = self;
504 addAction:[UIAlertAction actionWithTitle:@"Send"
505 style:UIAlertActionStyleDefault
506 handler:^(UIAlertAction * action) {
507 typeof(self) strongSelf = weakSelf;
509 NSArray * textfields = alertController.textFields;
510 UITextField * networkSSID = textfields[0];
511 UITextField * networkPassword = textfields[1];
512 if ([networkSSID.text length] > 0) {
513 CHIPSetDomainValueForKey(
514 kCHIPToolDefaultsDomain, kNetworkSSIDDefaultsKey, networkSSID.text);
517 if ([networkPassword.text length] > 0) {
518 CHIPSetDomainValueForKey(kCHIPToolDefaultsDomain, kNetworkPasswordDefaultsKey,
519 networkPassword.text);
521 NSLog(@"New SSID: %@ Password: %@", networkSSID.text, networkPassword.text);
523 [strongSelf.chipController sendWiFiCredentials:networkSSID.text
524 password:networkPassword.text];
527 [self presentViewController:alertController animated:YES completion:nil];
530 - (void)updateUIFields:(CHIPSetupPayload *)payload decimalString:(nullable NSString *)decimalString
533 _manualCodeLabel.hidden = NO;
534 _manualCodeLabel.text = decimalString;
535 _versionLabel.text = NOT_APPLICABLE_STRING;
536 _rendezVousInformation.text = NOT_APPLICABLE_STRING;
537 _serialNumber.text = NOT_APPLICABLE_STRING;
539 _manualCodeLabel.hidden = YES;
540 _versionLabel.text = [NSString stringWithFormat:@"%@", payload.version];
541 _rendezVousInformation.text = [NSString stringWithFormat:@"%lu", payload.rendezvousInformation];
542 if ([payload.serialNumber length] > 0) {
543 self->_serialNumber.text = payload.serialNumber;
545 self->_serialNumber.text = NOT_APPLICABLE_STRING;
549 _discriminatorLabel.text = [NSString stringWithFormat:@"%@", payload.discriminator];
550 _setupPinCodeLabel.text = [NSString stringWithFormat:@"%@", payload.setUpPINCode];
551 // TODO: Only display vid and pid if present
552 _vendorID.text = [NSString stringWithFormat:@"%@", payload.vendorID];
553 _productID.text = [NSString stringWithFormat:@"%@", payload.productID];
556 - (void)parseOptionalData:(CHIPSetupPayload *)payload
558 NSLog(@"Payload vendorID %@", payload.vendorID);
559 BOOL isSameVendorID = [payload.vendorID isEqualToNumber:[NSNumber numberWithInt:EXAMPLE_VENDOR_ID]];
560 if (!isSameVendorID) {
564 NSArray * optionalInfo = [payload getAllOptionalVendorData:nil];
565 for (CHIPOptionalQRCodeInfo * info in optionalInfo) {
566 NSNumber * tag = info.tag;
571 BOOL isTypeString = [info.infoType isEqualToNumber:[NSNumber numberWithInt:kOptionalQRCodeInfoTypeString]];
576 NSString * infoValue = info.stringValue;
577 switch (tag.unsignedCharValue) {
578 case EXAMPLE_VENDOR_TAG_IP:
579 if ([infoValue length] > MAX_IP_LEN) {
580 NSLog(@"Unexpected IP String... %@", infoValue);
589 - (void)handleRendezVous:(CHIPSetupPayload *)payload
591 switch (payload.rendezvousInformation) {
592 case kRendezvousInformationThread:
593 case kRendezvousInformationEthernet:
594 case kRendezvousInformationAllMask:
595 NSLog(@"Rendezvous Unknown");
597 case kRendezvousInformationWiFi:
598 NSLog(@"Rendezvous Wi-Fi");
599 [self handleRendezVousWiFi:[self getNetworkName:payload.discriminator]];
601 case kRendezvousInformationNone:
602 case kRendezvousInformationBLE:
603 NSLog(@"Rendezvous BLE");
604 [self handleRendezVousBLE:payload.discriminator.unsignedShortValue setupPINCode:payload.setUpPINCode.unsignedIntValue];
609 - (NSString *)getNetworkName:(NSNumber *)discriminator
611 NSString * peripheralDiscriminator = [NSString stringWithFormat:@"%04u", discriminator.unsignedShortValue];
612 NSString * peripheralFullName = [NSString stringWithFormat:@"%@%@", NETWORK_CHIP_PREFIX, peripheralDiscriminator];
613 return peripheralFullName;
616 - (void)handleRendezVousBLE:(uint16_t)discriminator setupPINCode:(uint32_t)setupPINCode
619 uint64_t deviceID = CHIPGetNextAvailableDeviceID();
620 if ([self.chipController pairDevice:deviceID discriminator:discriminator setupPINCode:setupPINCode error:&error]) {
622 CHIPSetNextAvailableDeviceID(deviceID);
626 - (void)handleRendezVousWiFi:(NSString *)name
628 NSString * message = [NSString stringWithFormat:@"SSID: %@\n\nUse WiFi Settings to connect to it.", name];
629 UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"SoftAP Detected"
631 preferredStyle:UIAlertControllerStyleActionSheet];
632 UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleCancel handler:nil];
633 [alert addAction:cancelAction];
634 [self presentViewController:alert animated:YES completion:nil];
639 - (BOOL)startScanning
642 AVCaptureDevice * captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
644 AVCaptureDeviceInput * input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
646 NSLog(@"Could not setup device input: %@", [error localizedDescription]);
650 AVCaptureMetadataOutput * captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
652 _captureSession = [[AVCaptureSession alloc] init];
653 [_captureSession addInput:input];
654 [_captureSession addOutput:captureMetadataOutput];
656 if (!_captureSessionQueue) {
657 _captureSessionQueue = dispatch_queue_create("captureSessionQueue", NULL);
660 [captureMetadataOutput setMetadataObjectsDelegate:self queue:_captureSessionQueue];
661 [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
663 _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
664 [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
665 [_videoPreviewLayer setFrame:_qrCodeViewPreview.layer.bounds];
666 [_qrCodeViewPreview.layer addSublayer:_videoPreviewLayer];
668 [_captureSession startRunning];
673 - (void)displayQRCodeInSetupPayloadView:(CHIPSetupPayload *)payload withError:(NSError *)error
676 [self showError:error];
678 [self showPayload:payload decimalString:nil];
682 - (void)scannedQRCode:(NSString *)qrCode
684 dispatch_async(dispatch_get_main_queue(), ^{
685 [self->_captureSession stopRunning];
686 [self->_session invalidateSession];
688 CHIPQRCodeSetupPayloadParser * parser = [[CHIPQRCodeSetupPayloadParser alloc] initWithBase41Representation:qrCode];
690 _setupPayload = [parser populatePayload:&error];
691 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
692 [self postScanningQRCodeState];
694 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, INDICATOR_DELAY), dispatch_get_main_queue(), ^{
695 [self displayQRCodeInSetupPayloadView:self->_setupPayload withError:error];
700 - (void)captureOutput:(AVCaptureOutput *)captureOutput
701 didOutputMetadataObjects:(NSArray *)metadataObjects
702 fromConnection:(AVCaptureConnection *)connection
704 if (metadataObjects != nil && [metadataObjects count] > 0) {
705 AVMetadataMachineReadableCodeObject * metadataObj = [metadataObjects objectAtIndex:0];
706 if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
707 [self scannedQRCode:[metadataObj stringValue]];
713 - (void)displayManualCodeInSetupPayloadView:(CHIPSetupPayload *)payload
714 decimalString:(NSString *)decimalString
715 withError:(NSError *)error
717 [self->_activityIndicator stopAnimating];
718 self->_activityIndicator.hidden = YES;
720 [self showError:error];
722 [self showPayload:payload decimalString:decimalString];
728 - (IBAction)startScanningQRCode:(id)sender
730 [self scanningStartState];
731 [self startScanning];
734 - (IBAction)stopScanningQRCode:(id)sender
736 [self qrCodeInitialState];
739 - (IBAction)resetView:(id)sender
741 // reset the view and remove any preferences that were stored from scanning the QRCode
742 [self manualCodeInitialState];
743 [self qrCodeInitialState];
746 - (IBAction)startScanningNFCTags:(id)sender
749 _session = [[NFCNDEFReaderSession alloc] initWithDelegate:self
750 queue:dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT)
751 invalidateAfterFirstRead:NO];
753 [_session beginSession];
756 - (IBAction)enteredManualCode:(id)sender
758 NSString * decimalString = _manualCodeTextField.text;
759 [self manualCodeEnteredStartState];
761 CHIPManualSetupPayloadParser * parser =
762 [[CHIPManualSetupPayloadParser alloc] initWithDecimalStringRepresentation:decimalString];
764 _setupPayload = [parser populatePayload:&error];
765 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, INDICATOR_DELAY), dispatch_get_main_queue(), ^{
766 [self displayManualCodeInSetupPayloadView:self->_setupPayload decimalString:decimalString withError:error];
768 [_manualCodeTextField resignFirstResponder];
771 @synthesize description;