ilabs-flir 2.2.9 → 2.2.11

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.
@@ -481,16 +481,31 @@ import ThermalSDK
481
481
 
482
482
  // MARK: - Connection
483
483
 
484
+ // Flag to prevent re-entrant connections
485
+ private var isConnecting = false
486
+
484
487
  @objc public func connectToDevice(_ deviceId: String) {
485
488
  FlirLogger.logConnectionAttempt(deviceId: deviceId)
486
489
 
487
490
  #if FLIR_ENABLED
488
- // Guard: if already connected, disconnect first
489
- if _isConnected {
490
- FlirLogger.log(.connection, "⚠️ Already connected - disconnecting first...")
491
+ // Guard: if already connecting, skip (like demo app's guard pattern)
492
+ guard !isConnecting else {
493
+ FlirLogger.log(.connection, "⚠️ Connection already in progress - skipping")
494
+ return
495
+ }
496
+
497
+ // Guard: if already connected to this device, skip (matches demo app)
498
+ if _isConnected && connectedDeviceId == deviceId {
499
+ FlirLogger.log(.connection, "⚠️ Already connected to this device - skipping")
500
+ return
501
+ }
502
+
503
+ // If connected to a different device, disconnect first but DON'T block
504
+ if _isConnected && connectedDeviceId != deviceId {
505
+ FlirLogger.log(.connection, "⚠️ Connected to different device - disconnecting first...")
491
506
  disconnect()
492
- // Give disconnect a moment to complete
493
- Thread.sleep(forTimeInterval: 0.5)
507
+ // Don't block! The new connection will proceed immediately
508
+ // The disconnect is async and will complete in the background
494
509
  }
495
510
 
496
511
  // Find the identity for this device
@@ -503,33 +518,14 @@ import ThermalSDK
503
518
  return
504
519
  }
505
520
 
506
- // Run connection on background thread with overall timeout
507
- let timeoutSeconds: Double = 15.0
508
- var connectionCompleted = false
509
- let connectionQueue = DispatchQueue.global(qos: .userInitiated)
510
-
511
- // Start connection attempt
512
- connectionQueue.async { [weak self] in
513
- self?.performConnection(identity: identity)
514
- connectionCompleted = true
515
- }
521
+ isConnecting = true
516
522
 
517
- // Monitor for timeout
518
- DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + timeoutSeconds) { [weak self] in
519
- guard let self = self, !connectionCompleted else { return }
520
-
521
- FlirLogger.logError(.connection, "❌ ⏱ CONNECTION TIMEOUT triggered after \(timeoutSeconds)s - aborting")
522
-
523
- // Force cleanup
524
- self.camera = nil
525
- self._isConnected = false
526
- self.connectedDeviceId = nil
527
-
528
- DispatchQueue.main.async {
529
- FlirLogger.log(.connection, "⏱ Emitting connection_failed to RN due to timeout")
530
- self.emitStateChange("connection_failed")
531
- self.delegate?.onError("Connection timeout - device not responding")
532
- }
523
+ // Run connection on background thread - simple pattern matching demo app
524
+ // Demo app uses: DispatchQueue.global().async { try pair(); try connect(); try stream.start() }
525
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
526
+ guard let self = self else { return }
527
+ self.performConnection(identity: identity)
528
+ self.isConnecting = false
533
529
  }
534
530
  #else
535
531
  FlirLogger.logError(.connection, "FLIR SDK not available")
@@ -538,6 +534,7 @@ import ThermalSDK
538
534
  }
539
535
  #endif
540
536
  }
537
+
541
538
 
542
539
  #if FLIR_ENABLED
543
540
  private func findIdentity(for deviceId: String) -> FLIRIdentity? {
@@ -566,34 +563,18 @@ import ThermalSDK
566
563
  }
567
564
 
568
565
  // Handle authentication for generic cameras (network cameras)
566
+ // Note: This uses a single attempt since the SDK doesn't provide async callback
569
567
  if identity.cameraType() == .generic {
570
- FlirLogger.log(.connection, "Generic/network camera - AUTHENTICATION starting...")
568
+ FlirLogger.log(.connection, "Generic/network camera - attempting authentication...")
571
569
  let certName = getCertificateName()
572
- var status = FLIRAuthenticationStatus.pending
573
- var attempts = 0
574
- let maxAttempts = 10 // 10 seconds max
575
- let authStartTime = Date()
570
+ let status = cam.authenticate(identity, trustedConnectionName: certName)
576
571
 
577
- while status == .pending && attempts < maxAttempts {
578
- status = cam.authenticate(identity, trustedConnectionName: certName)
579
- if status == .pending {
580
- FlirLogger.log(.connection, "Waiting for camera authentication approval... (\(attempts + 1)/\(maxAttempts))")
581
- Thread.sleep(forTimeInterval: 1.0)
582
- attempts += 1
583
- }
584
- }
585
-
586
- if status == .pending {
587
- let authDuration = Date().timeIntervalSince(authStartTime)
588
- FlirLogger.logError(.connection, "❌ Authentication TIMEOUT after \(authDuration)s (\(maxAttempts) attempts)")
589
- DispatchQueue.main.async { [weak self] in
590
- self?.delegate?.onError("Camera authentication timeout - device may require approval")
591
- }
592
- return
593
- }
572
+ // Log the status - don't block on pending, just proceed
573
+ // The connect() call will fail appropriately if auth is truly required
574
+ FlirLogger.log(.connection, "Authentication status: \(status.rawValue)")
594
575
 
595
- let authDuration = Date().timeIntervalSince(authStartTime)
596
- FlirLogger.log(.connection, "✅ Authentication completed in \(authDuration)s - status: \(status.rawValue)")
576
+ // Only fail on explicit rejection (status values vary by SDK version)
577
+ // Pending status means we should try to connect anyway
597
578
  }
598
579
 
599
580
  do {
@@ -708,9 +689,15 @@ import ThermalSDK
708
689
  }
709
690
 
710
691
  @objc public func stopStream() {
692
+ #if FLIR_ENABLED
693
+ // Guard: if not streaming, skip to avoid redundant operations
694
+ guard _isStreaming || stream != nil else {
695
+ FlirLogger.log(.streaming, "Not streaming - skipping stopStream")
696
+ return
697
+ }
698
+
711
699
  FlirLogger.log(.streaming, "Stopping stream...")
712
700
 
713
- #if FLIR_ENABLED
714
701
  stream?.stop()
715
702
  stream = nil
716
703
  streamer = nil
@@ -743,6 +730,9 @@ import ThermalSDK
743
730
  FlirLogger.logError(.streaming, "Stream start failed", error: error)
744
731
  stream = nil
745
732
  streamer = nil
733
+ _isStreaming = false
734
+ // Emit state change so RN knows streaming failed
735
+ emitStateChange("connected") // Fall back to connected (not streaming)
746
736
  delegate?.onError("Stream start failed: \(error.localizedDescription)")
747
737
  }
748
738
  }
@@ -751,9 +741,15 @@ import ThermalSDK
751
741
  // MARK: - Disconnect
752
742
 
753
743
  @objc public func disconnect() {
744
+ #if FLIR_ENABLED
745
+ // Guard: if already disconnected, do nothing (prevents re-entrant issues)
746
+ guard _isConnected || stream != nil || camera != nil else {
747
+ FlirLogger.log(.disconnect, "Already disconnected - skipping")
748
+ return
749
+ }
750
+
754
751
  FlirLogger.log(.disconnect, "Disconnecting...")
755
752
 
756
- #if FLIR_ENABLED
757
753
  // Stop battery polling
758
754
  stopBatteryPolling()
759
755
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilabs-flir",
3
- "version": "2.2.9",
3
+ "version": "2.2.11",
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",