ilabs-flir 2.1.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -310,23 +310,57 @@ import ThermalSDK
310
310
  discovery?.delegate = self
311
311
  }
312
312
 
313
- // Start discovery on all available interfaces
314
- let interfaces: FLIRCommunicationInterface = [
313
+ // Build interfaces based on available permissions
314
+ // Always include Lightning, USB, Wireless BLE, and Emulator
315
+ var interfaces: FLIRCommunicationInterface = [
315
316
  .lightning,
316
- .network,
317
317
  .flirOneWireless,
318
318
  .emulator
319
319
  ]
320
+
321
+ // Only add network discovery if NSLocalNetworkUsageDescription is present
322
+ // This prevents crashes/errors when user doesn't have iOS developer registration
323
+ // or hasn't declared network permission
324
+ if shouldEnableNetworkDiscovery() {
325
+ interfaces.insert(.network)
326
+ NSLog("[FlirManager] Network discovery enabled (NSLocalNetworkUsageDescription present)")
327
+ } else {
328
+ NSLog("[FlirManager] Network discovery disabled (no NSLocalNetworkUsageDescription)")
329
+ }
330
+
320
331
  discovery?.start(interfaces)
321
332
 
322
333
  emitStateChange("discovering")
323
- NSLog("[FlirManager] Discovery started on interfaces: Lightning, Network, FlirOneWireless, Emulator")
334
+ NSLog("[FlirManager] Discovery started on interfaces: Lightning, \(interfaces.contains(.network) ? "Network, " : "")FlirOneWireless, Emulator")
324
335
  #else
325
336
  NSLog("[FlirManager] FLIR SDK not available - discovery disabled")
326
337
  delegate?.onError("FLIR SDK not available")
327
338
  #endif
328
339
  }
329
340
 
341
+ /// Check if network discovery should be enabled based on Info.plist permission
342
+ private func shouldEnableNetworkDiscovery() -> Bool {
343
+ // Check for explicit override first
344
+ let key = "ilabsFlir.networkDiscoveryEnabled"
345
+ if UserDefaults.standard.object(forKey: key) != nil {
346
+ return UserDefaults.standard.bool(forKey: key)
347
+ }
348
+
349
+ // Safe default: require Local Network usage description to be present
350
+ if let desc = Bundle.main.object(forInfoDictionaryKey: "NSLocalNetworkUsageDescription") as? String,
351
+ !desc.isEmpty {
352
+ return true
353
+ }
354
+
355
+ return false
356
+ }
357
+
358
+ /// Allow explicit override of network discovery (called from React Native)
359
+ @objc public func setNetworkDiscoveryEnabled(_ enabled: Bool) {
360
+ UserDefaults.standard.set(enabled, forKey: "ilabsFlir.networkDiscoveryEnabled")
361
+ NSLog("[FlirManager] Network discovery override set to: \(enabled)")
362
+ }
363
+
330
364
  @objc public func stopDiscovery() {
331
365
  NSLog("[FlirManager] Stopping discovery...")
332
366
 
@@ -366,64 +400,72 @@ import ThermalSDK
366
400
  }
367
401
 
368
402
  private func performConnection(identity: FLIRIdentity) {
369
- do {
370
- if camera == nil {
371
- camera = FLIRCamera()
372
- camera?.delegate = self
403
+ // Use the proven connection pattern from FLIR SDK samples:
404
+ // FLIROneCameraSwift uses: pair(identity, code:) then connect()
405
+
406
+ if camera == nil {
407
+ camera = FLIRCamera()
408
+ camera?.delegate = self
409
+ }
410
+
411
+ guard let cam = camera else {
412
+ NSLog("[FlirManager] Failed to create FLIRCamera")
413
+ DispatchQueue.main.async { [weak self] in
414
+ self?.delegate?.onError("Failed to create camera instance")
373
415
  }
374
-
375
- // Handle authentication for generic cameras
376
- if identity.cameraType() == .generic {
377
- let certName = getCertificateName()
378
- var status = FLIRAuthenticationStatus.pending
379
- while status == .pending {
380
- status = camera!.authenticate(identity, trustedConnectionName: certName)
381
- if status == .pending {
382
- NSLog("[FlirManager] Waiting for camera authentication approval...")
383
- Thread.sleep(forTimeInterval: 1.0)
384
- }
416
+ return
417
+ }
418
+
419
+ // Handle authentication for generic cameras (network cameras)
420
+ if identity.cameraType() == .generic {
421
+ let certName = getCertificateName()
422
+ var status = FLIRAuthenticationStatus.pending
423
+ while status == .pending {
424
+ status = cam.authenticate(identity, trustedConnectionName: certName)
425
+ if status == .pending {
426
+ NSLog("[FlirManager] Waiting for camera authentication approval...")
427
+ Thread.sleep(forTimeInterval: 1.0)
385
428
  }
386
429
  }
430
+ NSLog("[FlirManager] Authentication status: \(status.rawValue)")
431
+ }
432
+
433
+ do {
434
+ // Step 1: Pair with identity (required for FLIR One devices)
435
+ // The code parameter is for BLE pairing, 0 for direct connection
436
+ try cam.pair(identity, code: 0)
437
+ NSLog("[FlirManager] Paired with: \(identity.deviceId())")
387
438
 
388
- // Connect (support multiple SDK signatures via ObjC runtime)
389
- var connectedOK = false
390
- if let cam = camera {
391
- if cam.responds(to: Selector(("connect:error:"))) {
392
- // Call connect:identity error:nil (ignore NSError pointer for compatibility)
393
- _ = cam.perform(Selector(("connect:error:")), with: identity, with: nil)
394
- connectedOK = true
395
- } else if cam.responds(to: Selector(("connect:"))) {
396
- _ = cam.perform(Selector(("connect:")), with: identity)
397
- connectedOK = true
398
- } else {
399
- NSLog("[FlirManager] No compatible connect API on FLIRCamera")
400
- }
401
- }
402
-
403
- if connectedOK {
404
- connectedIdentity = identity
405
- connectedDeviceId = identity.deviceId()
406
- connectedDeviceName = identity.deviceId()
407
- _isConnected = true
408
- NSLog("[FlirManager] Connected to: \(identity.deviceId())")
409
- } else {
410
- NSLog("[FlirManager] Connection failed: no compatible connect API")
411
- DispatchQueue.main.async { [weak self] in
412
- self?.delegate?.onError("Connection failed: unsupported SDK API")
413
- }
414
- return
439
+ // Step 2: Connect (no identity parameter - uses paired identity)
440
+ try cam.connect()
441
+ NSLog("[FlirManager] Connected to: \(identity.deviceId())")
442
+
443
+ // Update state
444
+ connectedIdentity = identity
445
+ connectedDeviceId = identity.deviceId()
446
+ connectedDeviceName = identity.deviceId()
447
+ _isConnected = true
448
+
449
+ // Get camera info if available
450
+ if let remoteControl = cam.getRemoteControl(),
451
+ let cameraInfo = try? remoteControl.getCameraInformation() {
452
+ NSLog("[FlirManager] Camera info: \(cameraInfo)")
415
453
  }
416
454
 
417
455
  // Get streams
418
- if let streams = camera?.getStreams(), !streams.isEmpty {
456
+ if let streams = cam.getStreams(), !streams.isEmpty {
419
457
  NSLog("[FlirManager] Found \(streams.count) streams")
420
458
 
421
- // Auto-start first thermal stream
422
- if let firstStream = streams.first {
423
- startStreamInternal(firstStream)
459
+ // Find and start the first thermal stream (preferred) or any stream
460
+ let thermalStream = streams.first { $0.isThermal } ?? streams.first
461
+ if let streamToStart = thermalStream {
462
+ startStreamInternal(streamToStart)
424
463
  }
464
+ } else {
465
+ NSLog("[FlirManager] No streams available on camera")
425
466
  }
426
467
 
468
+ // Notify delegate on main thread
427
469
  DispatchQueue.main.async { [weak self] in
428
470
  guard let self = self else { return }
429
471
  let deviceInfo = FlirDeviceInfo(
@@ -437,7 +479,9 @@ import ThermalSDK
437
479
  }
438
480
 
439
481
  } catch {
440
- NSLog("[FlirManager] Connection failed: \(error)")
482
+ NSLog("[FlirManager] Connection failed: \(error.localizedDescription)")
483
+ _isConnected = false
484
+ camera = nil
441
485
  DispatchQueue.main.async { [weak self] in
442
486
  self?.delegate?.onError("Connection failed: \(error.localizedDescription)")
443
487
  }
@@ -363,7 +363,7 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
363
363
 
364
364
  NSError *error = nil;
365
365
 
366
- // Handle authentication for generic cameras
366
+ // Handle authentication for generic cameras (network cameras)
367
367
  if ([identity cameraType] == FLIRCameraType_generic) {
368
368
  NSString *certName = [self getCertificateName];
369
369
  FLIRAuthenticationStatus status = pending;
@@ -374,9 +374,34 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
374
374
  [NSThread sleepForTimeInterval:1.0];
375
375
  }
376
376
  }
377
+ RCTLogInfo(@"[FlirModule] Authentication status: %d", (int)status);
377
378
  }
378
379
 
379
- BOOL connected = [self.camera connect:identity error:&error];
380
+ // Use the proven connection pattern from FLIR SDK samples:
381
+ // Step 1: Pair with identity (required for FLIR One devices)
382
+ @try {
383
+ [self.camera pair:identity code:0];
384
+ RCTLogInfo(@"[FlirModule] Paired with: %@", [identity deviceId]);
385
+ } @catch (NSException *exception) {
386
+ RCTLogError(@"[FlirModule] Pair failed: %@", exception.reason);
387
+ NSError *pairError = [NSError errorWithDomain:@"FlirModule" code:1001 userInfo:@{NSLocalizedDescriptionKey: exception.reason ?: @"Pair failed"}];
388
+ if (completion) completion(NO, pairError);
389
+ return;
390
+ }
391
+
392
+ // Step 2: Connect (no identity parameter - uses paired identity)
393
+ BOOL connected = NO;
394
+ @try {
395
+ [self.camera connect:&error];
396
+ connected = (error == nil);
397
+ if (connected) {
398
+ RCTLogInfo(@"[FlirModule] Connected to: %@", [identity deviceId]);
399
+ }
400
+ } @catch (NSException *exception) {
401
+ RCTLogError(@"[FlirModule] Connect exception: %@", exception.reason);
402
+ error = [NSError errorWithDomain:@"FlirModule" code:1002 userInfo:@{NSLocalizedDescriptionKey: exception.reason ?: @"Connect failed"}];
403
+ connected = NO;
404
+ }
380
405
 
381
406
  if (connected) {
382
407
  self.connectedIdentity = identity;
@@ -392,14 +417,25 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
392
417
  self.connectedDeviceName = displayName;
393
418
  self.isConnected = YES;
394
419
 
395
- RCTLogInfo(@"[FlirModule] Connected to: %@", [identity deviceId]);
420
+ RCTLogInfo(@"[FlirModule] Successfully connected to: %@", displayName);
396
421
 
397
- // Get available streams
422
+ // Get available streams and prefer thermal stream
398
423
  NSArray<FLIRStream *> *streams = [self.camera getStreams];
399
424
  if (streams.count > 0) {
400
425
  RCTLogInfo(@"[FlirModule] Found %lu streams", (unsigned long)streams.count);
401
- // Auto-start first stream
402
- [self startStreamInternal:streams[0]];
426
+
427
+ // Find thermal stream (preferred) or use first stream
428
+ FLIRStream *streamToStart = nil;
429
+ for (FLIRStream *stream in streams) {
430
+ if (stream.isThermal) {
431
+ streamToStart = stream;
432
+ break;
433
+ }
434
+ }
435
+ if (!streamToStart) {
436
+ streamToStart = streams[0];
437
+ }
438
+ [self startStreamInternal:streamToStart];
403
439
  }
404
440
 
405
441
  dispatch_async(dispatch_get_main_queue(), ^{
@@ -409,10 +445,12 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
409
445
  if (completion) completion(YES, nil);
410
446
  } else {
411
447
  RCTLogError(@"[FlirModule] Connection failed: %@", error.localizedDescription);
448
+ self.camera = nil;
412
449
  if (completion) completion(NO, error);
413
450
  }
414
451
  }
415
452
 
453
+
416
454
  - (NSString *)getCertificateName {
417
455
  NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier] ?: @"com.flir.app";
418
456
  NSString *key = [NSString stringWithFormat:@"%@-cert-name", bundleID];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilabs-flir",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "FLIR Thermal SDK for React Native - iOS & Android (bundled at compile time via postinstall)",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",