ilabs-flir 2.1.38 → 2.1.401

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.
@@ -88,14 +88,14 @@ object FlirManager {
88
88
  // Store react context for event emission if it's a React context
89
89
  // Always update if we get a valid ReactContext (in case previous was stale)
90
90
  if (context is ReactContext) {
91
- Log.d(TAG, "Storing ReactContext for event emission: ${context.javaClass.simpleName}")
91
+ Log.d(TAG, "[Flir-BRIDGE-LOAD] Storing ReactContext for event emission: ${context.javaClass.simpleName}")
92
92
  reactContext = context
93
93
  } else {
94
- Log.d(TAG, "Context is not ReactContext: ${context.javaClass.simpleName}")
94
+ Log.d(TAG, "[Flir-BRIDGE-LOAD] Context is not ReactContext: ${context.javaClass.simpleName}")
95
95
  }
96
96
 
97
97
  if (isInitialized) {
98
- Log.d(TAG, "Already initialized")
98
+ Log.d(TAG, "[Flir-BRIDGE-LOAD] Already initialized")
99
99
  return
100
100
  }
101
101
 
@@ -106,14 +106,14 @@ object FlirManager {
106
106
  sdkManager?.initialize()
107
107
 
108
108
  isInitialized = true
109
- Log.i(TAG, "FlirManager initialized")
109
+ Log.i(TAG, "[Flir-BRIDGE-LOAD] FlirManager initialized")
110
110
  }
111
111
 
112
112
  /**
113
113
  * Start scanning for devices (USB, Network, Emulator - ALL types)
114
114
  */
115
115
  fun startDiscovery(retry: Boolean = false) {
116
- Log.i(TAG, "startDiscovery(retry=$retry)")
116
+ Log.i(TAG, "[Flir-BRIDGE-DISCOVERY] startDiscovery(retry=$retry)")
117
117
 
118
118
  if (!isInitialized && appContext != null) {
119
119
  init(appContext!!)
@@ -141,7 +141,7 @@ object FlirManager {
141
141
  * Stop scanning
142
142
  */
143
143
  fun stopDiscovery() {
144
- Log.i(TAG, "stopDiscovery")
144
+ Log.i(TAG, "[Flir-BRIDGE-DISCOVERY] stopDiscovery")
145
145
  sdkManager?.stop()
146
146
  isScanning = false
147
147
  }
@@ -156,9 +156,10 @@ object FlirManager {
156
156
  val identity = devices.find { it.deviceId == deviceId }
157
157
 
158
158
  if (identity != null) {
159
+ Log.i(TAG, "[Flir-BRIDGE-CONNECTION] Connecting to found device: $deviceId")
159
160
  sdkManager?.connect(identity)
160
161
  } else {
161
- Log.e(TAG, "Device not found: $deviceId")
162
+ Log.e(TAG, "[Flir-BRIDGE-ERROR] Device not found: $deviceId")
162
163
  emitError("Device not found: $deviceId")
163
164
  }
164
165
  }
@@ -191,7 +192,7 @@ object FlirManager {
191
192
  * Stop streaming
192
193
  */
193
194
  fun stopStream() {
194
- Log.i(TAG, "stopStream")
195
+ Log.i(TAG, "[Flir-BRIDGE-STREAMING] stopStream")
195
196
  sdkManager?.stopStream()
196
197
  isStreaming = false
197
198
  }
@@ -200,7 +201,7 @@ object FlirManager {
200
201
  * Disconnect from current device
201
202
  */
202
203
  fun disconnect() {
203
- Log.i(TAG, "disconnect")
204
+ Log.i(TAG, "[Flir-BRIDGE-DISCONNECT] disconnect")
204
205
  sdkManager?.disconnect()
205
206
  isConnected = false
206
207
  isStreaming = false
@@ -318,7 +319,7 @@ object FlirManager {
318
319
  }
319
320
 
320
321
  override fun onDisconnected() {
321
- Log.i(TAG, "Disconnected")
322
+ Log.i(TAG, "[Flir-BRIDGE-DISCONNECT] Disconnected callback")
322
323
  isConnected = false
323
324
  isStreaming = false
324
325
  connectedDeviceId = null
@@ -354,7 +355,7 @@ object FlirManager {
354
355
  }
355
356
 
356
357
  override fun onBatteryUpdated(level: Int, isCharging: Boolean) {
357
- Log.d(TAG, "onBatteryUpdated: level=$level charging=$isCharging")
358
+ Log.d(TAG, "[Flir-BRIDGE-BATTERY] onBatteryUpdated: level=$level charging=$isCharging")
358
359
  emitBatteryState(level, isCharging)
359
360
  }
360
361
  }
@@ -382,10 +383,10 @@ object FlirManager {
382
383
  private fun emitDeviceState(state: String) {
383
384
  val ctx = reactContext
384
385
  if (ctx == null) {
385
- Log.e(TAG, "Cannot emit FlirDeviceConnected($state) - reactContext is null!")
386
+ Log.e(TAG, "[Flir-BRIDGE-ERROR] Cannot emit FlirDeviceConnected($state) - reactContext is null!")
386
387
  return
387
388
  }
388
- Log.d(TAG, "Emitting FlirDeviceConnected: $state")
389
+ Log.d(TAG, "[Flir-BRIDGE-CONNECTION] Emitting FlirDeviceConnected: $state")
389
390
  try {
390
391
  val params = Arguments.createMap().apply {
391
392
  putString("state", state)
@@ -428,9 +429,9 @@ object FlirManager {
428
429
 
429
430
  ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
430
431
  .emit("FlirDevicesFound", params)
431
- Log.d(TAG, "Successfully emitted FlirDevicesFound")
432
+ Log.d(TAG, "[Flir-BRIDGE-DISCOVERY] Successfully emitted FlirDevicesFound (${devices.size} devices)")
432
433
  } catch (e: Exception) {
433
- Log.e(TAG, "Failed to emit devices found", e)
434
+ Log.e(TAG, "[Flir-BRIDGE-ERROR] Failed to emit devices found", e)
434
435
  }
435
436
  }
436
437
 
@@ -175,9 +175,9 @@ public class FlirSdkManager {
175
175
 
176
176
  ThermalSdkAndroid.init(context);
177
177
  isInitialized = true;
178
- Log.d(TAG, "SDK initialized successfully");
178
+ Log.d(TAG, "[Flir-LOAD] SDK initialized successfully");
179
179
  } catch (Exception e) {
180
- Log.e(TAG, "Failed to initialize SDK", e);
180
+ Log.e(TAG, "[Flir-ERROR] Failed to initialize SDK", e);
181
181
  notifyError("SDK initialization failed: " + e.getMessage());
182
182
  }
183
183
  }
@@ -208,7 +208,7 @@ public class FlirSdkManager {
208
208
  isScanning = true;
209
209
  discoveredDevices.clear();
210
210
 
211
- Log.d(TAG, "Starting discovery for EMULATOR, NETWORK, USB...");
211
+ Log.d(TAG, "[Flir-DISCOVERY] Starting discovery for EMULATOR, NETWORK, USB...");
212
212
 
213
213
  try {
214
214
  DiscoveryFactory.getInstance().scan(
@@ -241,7 +241,7 @@ public class FlirSdkManager {
241
241
  }
242
242
 
243
243
  isScanning = false;
244
- Log.d(TAG, "Discovery stopped");
244
+ Log.d(TAG, "[Flir-DISCOVERY] Discovery stopped");
245
245
  }
246
246
 
247
247
  /**
@@ -273,7 +273,7 @@ public class FlirSdkManager {
273
273
  camera = new Camera();
274
274
  camera.connect(identity, connectionStatusListener, new ConnectParameters());
275
275
 
276
- Log.d(TAG, "Connected to camera");
276
+ Log.d(TAG, "[Flir-CONNECTION] Connected to camera: " + identity.deviceId);
277
277
 
278
278
  if (listener != null) {
279
279
  listener.onConnected(identity);
@@ -309,7 +309,7 @@ public class FlirSdkManager {
309
309
  listener.onDisconnected();
310
310
  }
311
311
 
312
- Log.d(TAG, "Disconnected");
312
+ Log.d(TAG, "[Flir-DISCONNECT] Disconnected");
313
313
  }
314
314
 
315
315
  /**
@@ -397,10 +397,10 @@ public class FlirSdkManager {
397
397
  notifyError("Stream error: " + error);
398
398
  });
399
399
 
400
- Log.d(TAG, "Streaming started");
400
+ Log.d(TAG, "[Flir-STREAMING] Streaming started");
401
401
 
402
402
  } catch (Exception e) {
403
- Log.e(TAG, "Failed to start stream", e);
403
+ Log.e(TAG, "[Flir-ERROR] Failed to start stream", e);
404
404
  notifyError("Stream failed: " + e.getMessage());
405
405
  }
406
406
  });
@@ -425,7 +425,7 @@ public class FlirSdkManager {
425
425
  isProcessingFrame = false;
426
426
  lastFrameProcessedMs = 0;
427
427
 
428
- Log.d(TAG, "Streaming stopped");
428
+ Log.d(TAG, "[Flir-STREAMING] Streaming stopped");
429
429
  }
430
430
 
431
431
  /**
@@ -522,12 +522,13 @@ public class FlirSdkManager {
522
522
  streamer.withThermalImage(thermalImage -> {
523
523
  thermalImage.setPalette(palette);
524
524
  });
525
- Log.d(TAG, "Palette set to: " + paletteName);
525
+ Log.d(TAG, "[Flir-STREAMING] Palette set to: " + paletteName);
526
526
  }
527
527
  } catch (Exception e) {
528
528
  Log.e(TAG, "Error setting palette", e);
529
529
  }
530
530
  });
531
+
531
532
  }
532
533
 
533
534
  /**
@@ -644,8 +645,8 @@ public class FlirSdkManager {
644
645
  @Override
645
646
  public void onCameraFound(DiscoveredCamera discoveredCamera) {
646
647
  Identity identity = discoveredCamera.getIdentity();
647
- Log.d(TAG, "Device found: " + identity.deviceId +
648
- " type=" + identity.communicationInterface);
648
+ Log.d(TAG,
649
+ "[Flir-DISCOVERY] Device found: " + identity.deviceId + " type=" + identity.communicationInterface);
649
650
 
650
651
  // Add to list if not already present
651
652
  synchronized (discoveredDevices) {
@@ -669,7 +670,7 @@ public class FlirSdkManager {
669
670
 
670
671
  @Override
671
672
  public void onCameraLost(Identity identity) {
672
- Log.d(TAG, "Device lost: " + identity.deviceId);
673
+ Log.d(TAG, "[Flir-DISCOVERY] Device lost: " + identity.deviceId);
673
674
 
674
675
  synchronized (discoveredDevices) {
675
676
  discoveredDevices.removeIf(d -> d.deviceId.equals(identity.deviceId));
@@ -682,13 +683,13 @@ public class FlirSdkManager {
682
683
 
683
684
  @Override
684
685
  public void onDiscoveryError(CommunicationInterface iface, ErrorCode error) {
685
- Log.e(TAG, "Discovery error: " + iface + " - " + error);
686
+ Log.e(TAG, "[Flir-ERROR] Discovery error: " + iface + " - " + error);
686
687
  notifyError("Discovery error: " + error);
687
688
  }
688
689
 
689
690
  @Override
690
691
  public void onDiscoveryFinished(CommunicationInterface iface) {
691
- Log.d(TAG, "Discovery finished for: " + iface);
692
+ Log.d(TAG, "[Flir-DISCOVERY] Discovery finished for: " + iface);
692
693
  }
693
694
  };
694
695
 
@@ -739,6 +740,9 @@ public class FlirSdkManager {
739
740
  if (level != lastPolledBatteryLevel || charging != lastPolledCharging) {
740
741
  lastPolledBatteryLevel = level;
741
742
  lastPolledCharging = charging;
743
+
744
+ Log.d(TAG, String.format("[Flir-BATTERY] Level: %d%%, Charging: %b", level, charging));
745
+
742
746
  if (listener != null) {
743
747
  try {
744
748
  listener.onBatteryUpdated(level, charging);
@@ -0,0 +1,135 @@
1
+ //
2
+ // FlirLogger.swift
3
+ // Flir
4
+ //
5
+ // Comprehensive lifecycle logging for FLIR camera operations
6
+ // Helps debug: load → discovery → connection → streaming → frame → battery
7
+ //
8
+
9
+ import Foundation
10
+
11
+ /// Lifecycle stages for FLIR operations
12
+ @objc public enum FlirLifecycleStage: Int {
13
+ case load // SDK initialization
14
+ case discovery // Device scanning
15
+ case connection // Device pairing/connecting
16
+ case streaming // Stream start/stop
17
+ case frame // Frame receiving
18
+ case battery // Battery polling
19
+ case error // Error states
20
+ case disconnect // Disconnection events
21
+
22
+ var tag: String {
23
+ switch self {
24
+ case .load: return "LOAD"
25
+ case .discovery: return "DISCOVERY"
26
+ case .connection: return "CONNECTION"
27
+ case .streaming: return "STREAMING"
28
+ case .frame: return "FRAME"
29
+ case .battery: return "BATTERY"
30
+ case .error: return "ERROR"
31
+ case .disconnect: return "DISCONNECT"
32
+ }
33
+ }
34
+ }
35
+
36
+ /// Centralized logger for FLIR operations with consistent formatting
37
+ @objc public class FlirLogger: NSObject {
38
+
39
+ /// Frame counter for periodic frame logging
40
+ private static var frameCount: Int = 0
41
+ private static let frameLogInterval: Int = 100 // Log every 100 frames
42
+
43
+ /// Log a message at a specific lifecycle stage
44
+ @objc public static func log(_ stage: FlirLifecycleStage, _ message: String) {
45
+ let timestamp = Date()
46
+ let formatter = DateFormatter()
47
+ formatter.dateFormat = "HH:mm:ss.SSS"
48
+ let timeStr = formatter.string(from: timestamp)
49
+ NSLog("[Flir-\(stage.tag)] [\(timeStr)] \(message)")
50
+ }
51
+
52
+ /// Log an error with optional Error object
53
+ @objc public static func logError(_ stage: FlirLifecycleStage, _ message: String, error: Error? = nil) {
54
+ let errorDesc = error?.localizedDescription ?? "no error details"
55
+ log(stage, "❌ \(message) - \(errorDesc)")
56
+ }
57
+
58
+ /// Log frame received (rate-limited to avoid log spam)
59
+ @objc public static func logFrame(width: Int, height: Int) {
60
+ logFrame(width: width, height: height, temperature: 0)
61
+ }
62
+
63
+ /// Log frame received with temperature (rate-limited to avoid log spam)
64
+ @objc public static func logFrame(width: Int, height: Int, temperature: Double) {
65
+ frameCount += 1
66
+ if frameCount % frameLogInterval == 0 {
67
+ var msg = "Frame #\(frameCount) received (\(width)x\(height))"
68
+ if !temperature.isNaN {
69
+ msg += " temp=\(String(format: "%.1f", temperature))°C"
70
+ }
71
+ log(.frame, msg)
72
+ }
73
+ }
74
+
75
+ /// Internal implementation for logFrame with optional temperature
76
+ private static func logFrame(width: Int, height: Int, temperature: Double?) {
77
+ frameCount += 1
78
+ if frameCount % frameLogInterval == 0 {
79
+ var msg = "Frame #\(frameCount) received (\(width)x\(height))"
80
+ if let temp = temperature, !temp.isNaN {
81
+ msg += " temp=\(String(format: "%.1f", temp))°C"
82
+ }
83
+ log(.frame, msg)
84
+ }
85
+ }
86
+
87
+ /// Reset frame counter (call on disconnect)
88
+ @objc public static func resetFrameCounter() {
89
+ frameCount = 0
90
+ }
91
+
92
+ /// Log discovery interface status (important for debugging network vs lightning)
93
+ @objc public static func logDiscoveryInterfaces(lightning: Bool, network: Bool, wireless: Bool, emulator: Bool) {
94
+ var interfaces: [String] = []
95
+ if lightning { interfaces.append("Lightning") }
96
+ if network { interfaces.append("Network") }
97
+ if wireless { interfaces.append("FlirOneWireless") }
98
+ if emulator { interfaces.append("Emulator") }
99
+
100
+ log(.discovery, "Starting discovery on interfaces: \(interfaces.joined(separator: ", "))")
101
+
102
+ if !network {
103
+ log(.discovery, "⚠️ Network discovery DISABLED - requires paid iOS Developer License with NSLocalNetworkUsageDescription")
104
+ }
105
+ }
106
+
107
+ /// Log device found with details
108
+ @objc public static func logDeviceFound(deviceId: String, name: String, type: String, isEmulator: Bool) {
109
+ var msg = "Device found: '\(name)' (id=\(deviceId), type=\(type))"
110
+ if isEmulator {
111
+ msg += " [EMULATOR]"
112
+ }
113
+ log(.discovery, msg)
114
+ }
115
+
116
+ /// Log connection attempt
117
+ @objc public static func logConnectionAttempt(deviceId: String) {
118
+ log(.connection, "Attempting connection to: \(deviceId)")
119
+ }
120
+
121
+ /// Log connection success with stream info
122
+ @objc public static func logConnectionSuccess(deviceId: String, streamCount: Int, hasThermal: Bool) {
123
+ log(.connection, "✅ Connected to: \(deviceId) - \(streamCount) stream(s), thermal=\(hasThermal)")
124
+ }
125
+
126
+ /// Log battery state
127
+ @objc public static func logBattery(level: Int, isCharging: Bool) {
128
+ if level >= 0 {
129
+ let chargingStr = isCharging ? "charging" : "not charging"
130
+ log(.battery, "Level: \(level)%, \(chargingStr)")
131
+ } else {
132
+ log(.battery, "Battery info unavailable")
133
+ }
134
+ }
135
+ }
@@ -75,6 +75,11 @@ import ThermalSDK
75
75
  private var activeClients: Set<String> = []
76
76
  private var shutdownWorkItem: DispatchWorkItem? = nil
77
77
 
78
+ // Battery polling timer (like Android)
79
+ private var batteryPollingTimer: Timer?
80
+ private var lastPolledBatteryLevel: Int = -1
81
+ private var lastPolledCharging: Bool = false
82
+
78
83
  #if FLIR_ENABLED
79
84
  private var discovery: FLIRDiscovery?
80
85
  private var camera: FLIRCamera?
@@ -85,7 +90,12 @@ import ThermalSDK
85
90
 
86
91
  private override init() {
87
92
  super.init()
88
- NSLog("[FlirManager] Initialized")
93
+ FlirLogger.log(.load, "FlirManager singleton initialized")
94
+ #if FLIR_ENABLED
95
+ FlirLogger.log(.load, "✅ FLIR SDK is ENABLED (ThermalSDK available)")
96
+ #else
97
+ FlirLogger.log(.load, "⚠️ FLIR SDK is DISABLED (FLIR_ENABLED not defined)")
98
+ #endif
89
99
  }
90
100
 
91
101
  // MARK: - Public State Accessors
@@ -335,11 +345,11 @@ import ThermalSDK
335
345
  // MARK: - Discovery
336
346
 
337
347
  @objc public func startDiscovery() {
338
- NSLog("[FlirManager] Starting discovery...")
348
+ FlirLogger.log(.discovery, "Starting discovery...")
339
349
 
340
350
  #if FLIR_ENABLED
341
351
  if isScanning {
342
- NSLog("[FlirManager] Already scanning")
352
+ FlirLogger.log(.discovery, "Already scanning - skipping")
343
353
  return
344
354
  }
345
355
 
@@ -349,6 +359,7 @@ import ThermalSDK
349
359
  if discovery == nil {
350
360
  discovery = FLIRDiscovery()
351
361
  discovery?.delegate = self
362
+ FlirLogger.log(.discovery, "Created FLIRDiscovery instance")
352
363
  }
353
364
 
354
365
  // Build interfaces based on available permissions
@@ -362,19 +373,24 @@ import ThermalSDK
362
373
  // Only add network discovery if NSLocalNetworkUsageDescription is present
363
374
  // This prevents crashes/errors when user doesn't have iOS developer registration
364
375
  // or hasn't declared network permission
365
- if shouldEnableNetworkDiscovery() {
376
+ let networkEnabled = shouldEnableNetworkDiscovery()
377
+ if networkEnabled {
366
378
  interfaces.insert(.network)
367
- NSLog("[FlirManager] Network discovery enabled (NSLocalNetworkUsageDescription present)")
368
- } else {
369
- NSLog("[FlirManager] Network discovery disabled (no NSLocalNetworkUsageDescription)")
370
379
  }
371
380
 
381
+ // Log which interfaces are enabled (important for debugging)
382
+ FlirLogger.logDiscoveryInterfaces(
383
+ lightning: true,
384
+ network: networkEnabled,
385
+ wireless: true,
386
+ emulator: true
387
+ )
388
+
372
389
  discovery?.start(interfaces)
373
390
 
374
391
  emitStateChange("discovering")
375
- NSLog("[FlirManager] Discovery started on interfaces: Lightning, \(interfaces.contains(.network) ? "Network, " : "")FlirOneWireless, Emulator")
376
392
  #else
377
- NSLog("[FlirManager] FLIR SDK not available - discovery disabled")
393
+ FlirLogger.logError(.discovery, "FLIR SDK not available - discovery disabled")
378
394
  delegate?.onError("FLIR SDK not available")
379
395
  #endif
380
396
  }
@@ -399,28 +415,28 @@ import ThermalSDK
399
415
  /// Allow explicit override of network discovery (called from React Native)
400
416
  @objc public func setNetworkDiscoveryEnabled(_ enabled: Bool) {
401
417
  UserDefaults.standard.set(enabled, forKey: "ilabsFlir.networkDiscoveryEnabled")
402
- NSLog("[FlirManager] Network discovery override set to: \(enabled)")
418
+ FlirLogger.log(.discovery, "Network discovery override set to: \(enabled)")
403
419
  }
404
420
 
405
421
  @objc public func stopDiscovery() {
406
- NSLog("[FlirManager] Stopping discovery...")
422
+ FlirLogger.log(.discovery, "Stopping discovery...")
407
423
 
408
424
  #if FLIR_ENABLED
409
425
  discovery?.stop()
410
426
  isScanning = false
411
- NSLog("[FlirManager] Discovery stopped")
427
+ FlirLogger.log(.discovery, "Discovery stopped")
412
428
  #endif
413
429
  }
414
430
 
415
431
  // MARK: - Connection
416
432
 
417
433
  @objc public func connectToDevice(_ deviceId: String) {
418
- NSLog("[FlirManager] Connecting to device: \(deviceId)")
434
+ FlirLogger.logConnectionAttempt(deviceId: deviceId)
419
435
 
420
436
  #if FLIR_ENABLED
421
437
  // Find the identity for this device
422
438
  guard let identity = findIdentity(for: deviceId) else {
423
- NSLog("[FlirManager] Device not found: \(deviceId)")
439
+ FlirLogger.logError(.connection, "Device not found in identity map: \(deviceId)")
424
440
  delegate?.onError("Device not found: \(deviceId)")
425
441
  return
426
442
  }
@@ -429,6 +445,7 @@ import ThermalSDK
429
445
  self?.performConnection(identity: identity)
430
446
  }
431
447
  #else
448
+ FlirLogger.logError(.connection, "FLIR SDK not available")
432
449
  delegate?.onError("FLIR SDK not available")
433
450
  #endif
434
451
  }
@@ -443,14 +460,16 @@ import ThermalSDK
443
460
  private func performConnection(identity: FLIRIdentity) {
444
461
  // Use the proven connection pattern from FLIR SDK samples:
445
462
  // FLIROneCameraSwift uses: pair(identity, code:) then connect()
463
+ FlirLogger.log(.connection, "performConnection starting for: \(identity.deviceId())")
446
464
 
447
465
  if camera == nil {
448
466
  camera = FLIRCamera()
449
467
  camera?.delegate = self
468
+ FlirLogger.log(.connection, "Created new FLIRCamera instance")
450
469
  }
451
470
 
452
471
  guard let cam = camera else {
453
- NSLog("[FlirManager] Failed to create FLIRCamera")
472
+ FlirLogger.logError(.connection, "Failed to create FLIRCamera instance")
454
473
  DispatchQueue.main.async { [weak self] in
455
474
  self?.delegate?.onError("Failed to create camera instance")
456
475
  }
@@ -459,27 +478,30 @@ import ThermalSDK
459
478
 
460
479
  // Handle authentication for generic cameras (network cameras)
461
480
  if identity.cameraType() == .generic {
481
+ FlirLogger.log(.connection, "Generic/network camera - starting authentication...")
462
482
  let certName = getCertificateName()
463
483
  var status = FLIRAuthenticationStatus.pending
464
484
  while status == .pending {
465
485
  status = cam.authenticate(identity, trustedConnectionName: certName)
466
486
  if status == .pending {
467
- NSLog("[FlirManager] Waiting for camera authentication approval...")
487
+ FlirLogger.log(.connection, "Waiting for camera authentication approval...")
468
488
  Thread.sleep(forTimeInterval: 1.0)
469
489
  }
470
490
  }
471
- NSLog("[FlirManager] Authentication status: \(status.rawValue)")
491
+ FlirLogger.log(.connection, "Authentication status: \(status.rawValue)")
472
492
  }
473
493
 
474
494
  do {
475
495
  // Step 1: Pair with identity (required for FLIR One devices)
476
496
  // The code parameter is for BLE pairing, 0 for direct connection
497
+ FlirLogger.log(.connection, "Step 1: Pairing with device...")
477
498
  try cam.pair(identity, code: 0)
478
- NSLog("[FlirManager] Paired with: \(identity.deviceId())")
499
+ FlirLogger.log(.connection, " Paired successfully with: \(identity.deviceId())")
479
500
 
480
501
  // Step 2: Connect (no identity parameter - uses paired identity)
502
+ FlirLogger.log(.connection, "Step 2: Connecting to device...")
481
503
  try cam.connect()
482
- NSLog("[FlirManager] Connected to: \(identity.deviceId())")
504
+ FlirLogger.log(.connection, " Connected successfully to: \(identity.deviceId())")
483
505
 
484
506
  // Update state
485
507
  connectedIdentity = identity
@@ -490,23 +512,27 @@ import ThermalSDK
490
512
  // Get camera info if available
491
513
  if let remoteControl = cam.getRemoteControl(),
492
514
  let cameraInfo = try? remoteControl.getCameraInformation() {
493
- NSLog("[FlirManager] Camera info: \(cameraInfo)")
515
+ FlirLogger.log(.connection, "Camera info: \(cameraInfo)")
494
516
  }
495
517
 
496
518
  // Get streams
497
519
  let streams = cam.getStreams()
520
+ let thermalCount = streams.filter { $0.isThermal }.count
521
+ FlirLogger.logConnectionSuccess(deviceId: identity.deviceId(), streamCount: streams.count, hasThermal: thermalCount > 0)
522
+
498
523
  if !streams.isEmpty {
499
- NSLog("[FlirManager] Found \(streams.count) streams")
500
-
501
524
  // Find and start the first thermal stream (preferred) or any stream
502
525
  let thermalStream = streams.first { $0.isThermal } ?? streams.first
503
526
  if let streamToStart = thermalStream {
504
527
  startStreamInternal(streamToStart)
505
528
  }
506
529
  } else {
507
- NSLog("[FlirManager] No streams available on camera")
530
+ FlirLogger.log(.streaming, "⚠️ No streams available on camera")
508
531
  }
509
532
 
533
+ // Start battery polling (like Android does)
534
+ startBatteryPolling()
535
+
510
536
  // Notify delegate on main thread
511
537
  DispatchQueue.main.async { [weak self] in
512
538
  guard let self = self else { return }
@@ -521,7 +547,7 @@ import ThermalSDK
521
547
  }
522
548
 
523
549
  } catch {
524
- NSLog("[FlirManager] Connection failed: \(error.localizedDescription)")
550
+ FlirLogger.logError(.connection, "Connection failed", error: error)
525
551
  _isConnected = false
526
552
  camera = nil
527
553
  DispatchQueue.main.async { [weak self] in
@@ -558,7 +584,7 @@ import ThermalSDK
558
584
  @objc public func startStream() {
559
585
  #if FLIR_ENABLED
560
586
  guard let streams = camera?.getStreams(), !streams.isEmpty else {
561
- NSLog("[FlirManager] No streams available")
587
+ FlirLogger.log(.streaming, "⚠️ No streams available")
562
588
  return
563
589
  }
564
590
  startStreamInternal(streams[0])
@@ -566,7 +592,7 @@ import ThermalSDK
566
592
  }
567
593
 
568
594
  @objc public func stopStream() {
569
- NSLog("[FlirManager] Stopping stream...")
595
+ FlirLogger.log(.streaming, "Stopping stream...")
570
596
 
571
597
  #if FLIR_ENABLED
572
598
  stream?.stop()
@@ -574,18 +600,20 @@ import ThermalSDK
574
600
  streamer = nil
575
601
  _isStreaming = false
576
602
  emitStateChange("connected")
603
+ FlirLogger.log(.streaming, "Stream stopped")
577
604
  #endif
578
605
  }
579
606
 
580
607
  #if FLIR_ENABLED
581
608
  private func startStreamInternal(_ newStream: FLIRStream) {
582
- NSLog("[FlirManager] Starting stream...")
609
+ FlirLogger.log(.streaming, "Starting stream (thermal=\(newStream.isThermal))...")
583
610
 
584
611
  stream?.stop()
585
612
  stream = newStream
586
613
 
587
614
  if newStream.isThermal {
588
615
  streamer = FLIRThermalStreamer(stream: newStream)
616
+ FlirLogger.log(.streaming, "Created FLIRThermalStreamer for thermal stream")
589
617
  }
590
618
 
591
619
  newStream.delegate = self
@@ -594,9 +622,9 @@ import ThermalSDK
594
622
  try newStream.start()
595
623
  _isStreaming = true
596
624
  emitStateChange("streaming")
597
- NSLog("[FlirManager] Stream started (thermal: \(newStream.isThermal))")
625
+ FlirLogger.log(.streaming, " Stream started successfully (thermal=\(newStream.isThermal))")
598
626
  } catch {
599
- NSLog("[FlirManager] Stream start failed: \(error)")
627
+ FlirLogger.logError(.streaming, "Stream start failed", error: error)
600
628
  stream = nil
601
629
  streamer = nil
602
630
  delegate?.onError("Stream start failed: \(error.localizedDescription)")
@@ -607,9 +635,12 @@ import ThermalSDK
607
635
  // MARK: - Disconnect
608
636
 
609
637
  @objc public func disconnect() {
610
- NSLog("[FlirManager] Disconnecting...")
638
+ FlirLogger.log(.disconnect, "Disconnecting...")
611
639
 
612
640
  #if FLIR_ENABLED
641
+ // Stop battery polling
642
+ stopBatteryPolling()
643
+
613
644
  stopStream()
614
645
  camera?.disconnect()
615
646
  camera = nil
@@ -620,14 +651,19 @@ import ThermalSDK
620
651
  _isStreaming = false
621
652
  _latestImage = nil
622
653
 
654
+ // Reset frame counter for next connection
655
+ FlirLogger.resetFrameCounter()
656
+
623
657
  DispatchQueue.main.async { [weak self] in
624
658
  self?.delegate?.onDeviceDisconnected()
625
659
  self?.emitStateChange("disconnected")
626
660
  }
661
+ FlirLogger.log(.disconnect, "Disconnected successfully")
627
662
  #endif
628
663
  }
629
664
 
630
665
  @objc public func stop() {
666
+ FlirLogger.log(.disconnect, "stop() called - full shutdown")
631
667
  stopStream()
632
668
  disconnect()
633
669
  stopDiscovery()
@@ -658,10 +694,56 @@ import ThermalSDK
658
694
  return lastTemperature
659
695
  }
660
696
 
697
+ // MARK: - Battery Polling (like Android)
698
+
699
+ private func startBatteryPolling() {
700
+ FlirLogger.log(.battery, "Starting battery polling timer (5s interval)")
701
+
702
+ // Cancel any existing timer
703
+ stopBatteryPolling()
704
+
705
+ // Schedule timer on main run loop
706
+ DispatchQueue.main.async { [weak self] in
707
+ self?.batteryPollingTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
708
+ self?.pollBatteryState()
709
+ }
710
+ // Run immediately once
711
+ self?.pollBatteryState()
712
+ }
713
+ }
714
+
715
+ private func stopBatteryPolling() {
716
+ if batteryPollingTimer != nil {
717
+ FlirLogger.log(.battery, "Stopping battery polling timer")
718
+ }
719
+ batteryPollingTimer?.invalidate()
720
+ batteryPollingTimer = nil
721
+ }
722
+
723
+ private func pollBatteryState() {
724
+ let level = getBatteryLevel()
725
+ let charging = isBatteryCharging()
726
+
727
+ // Only log and emit if values changed
728
+ if level != lastPolledBatteryLevel || charging != lastPolledCharging {
729
+ lastPolledBatteryLevel = level
730
+ lastPolledCharging = charging
731
+
732
+ FlirLogger.logBattery(level: level, isCharging: charging)
733
+
734
+ // Emit to delegate/RN via notification
735
+ NotificationCenter.default.post(
736
+ name: Notification.Name("FlirBatteryUpdated"),
737
+ object: nil,
738
+ userInfo: ["level": level, "isCharging": charging]
739
+ )
740
+ }
741
+ }
742
+
661
743
  // MARK: - Emulator
662
744
 
663
745
  @objc public func startEmulator(type: String) {
664
- NSLog("[FlirManager] Starting emulator: \(type)")
746
+ FlirLogger.log(.connection, "Starting emulator with type: \(type)")
665
747
 
666
748
  #if FLIR_ENABLED
667
749
  // Create emulator identity
@@ -671,6 +753,7 @@ import ThermalSDK
671
753
  } else if type.lowercased().contains("pro") {
672
754
  cameraType = .flirOneEdgePro
673
755
  }
756
+ FlirLogger.log(.connection, "Emulator camera type: \(cameraType)")
674
757
 
675
758
  if let emulatorIdentity = FLIRIdentity(emulatorType: cameraType) {
676
759
  discoveredDevices.append(FlirDeviceInfo(
@@ -680,11 +763,15 @@ import ThermalSDK
680
763
  isEmulator: true
681
764
  ))
682
765
  identityMap[emulatorIdentity.deviceId()] = emulatorIdentity
766
+ FlirLogger.log(.connection, "Emulator identity created: \(emulatorIdentity.deviceId())")
683
767
 
684
768
  // Auto-connect to emulator
685
769
  performConnection(identity: emulatorIdentity)
770
+ } else {
771
+ FlirLogger.logError(.connection, "Failed to create emulator identity for type: \(type)")
686
772
  }
687
773
  #else
774
+ FlirLogger.logError(.connection, "FLIR SDK not available - emulator disabled")
688
775
  delegate?.onError("FLIR SDK not available - emulator disabled")
689
776
  #endif
690
777
  }
@@ -764,8 +851,12 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
764
851
  public func cameraDiscovered(_ discoveredCamera: FLIRDiscoveredCamera) {
765
852
  let identity = discoveredCamera.identity
766
853
  let deviceId = identity.deviceId()
854
+ let displayName = discoveredCamera.displayName ?? deviceId
855
+ let commType = communicationInterfaceName(identity.communicationInterface())
856
+ let isEmulator = identity.communicationInterface() == .emulator
767
857
 
768
- NSLog("[FlirManager] Camera discovered: \(deviceId)")
858
+ // Use FlirLogger for consistent logging
859
+ FlirLogger.logDeviceFound(deviceId: deviceId, name: displayName, type: commType, isEmulator: isEmulator)
769
860
 
770
861
  // Store identity for later connection
771
862
  identityMap[deviceId] = identity
@@ -773,14 +864,15 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
773
864
  // Create device info
774
865
  let deviceInfo = FlirDeviceInfo(
775
866
  deviceId: deviceId,
776
- name: discoveredCamera.displayName ?? deviceId,
777
- communicationType: communicationInterfaceName(identity.communicationInterface()),
778
- isEmulator: identity.communicationInterface() == .emulator
867
+ name: displayName,
868
+ communicationType: commType,
869
+ isEmulator: isEmulator
779
870
  )
780
871
 
781
872
  // Add to discovered list if not already present
782
873
  if !discoveredDevices.contains(where: { $0.deviceId == deviceId }) {
783
874
  discoveredDevices.append(deviceInfo)
875
+ FlirLogger.log(.discovery, "Total discovered devices: \(discoveredDevices.count)")
784
876
  }
785
877
 
786
878
  // Notify delegate
@@ -792,13 +884,14 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
792
884
 
793
885
  public func cameraLost(_ cameraIdentity: FLIRIdentity) {
794
886
  let deviceId = cameraIdentity.deviceId()
795
- NSLog("[FlirManager] Camera lost: \(deviceId)")
887
+ FlirLogger.log(.discovery, "Camera lost: \(deviceId)")
796
888
 
797
889
  identityMap.removeValue(forKey: deviceId)
798
890
  discoveredDevices.removeAll { $0.deviceId == deviceId }
799
891
 
800
892
  // If this was our connected device, handle disconnect
801
893
  if connectedDeviceId == deviceId {
894
+ FlirLogger.log(.disconnect, "Lost connected device - triggering disconnect")
802
895
  disconnect()
803
896
  }
804
897
 
@@ -809,7 +902,7 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
809
902
  }
810
903
 
811
904
  public func discoveryError(_ error: String, netServiceError nsnetserviceserror: Int32, on iface: FLIRCommunicationInterface) {
812
- NSLog("[FlirManager] Discovery error: \(error) (\(nsnetserviceserror)) on interface: \(iface)")
905
+ FlirLogger.logError(.discovery, "Discovery error: \(error) (code=\(nsnetserviceserror)) on interface: \(iface)")
813
906
 
814
907
  DispatchQueue.main.async { [weak self] in
815
908
  self?.delegate?.onError("Discovery error: \(error)")
@@ -817,7 +910,7 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
817
910
  }
818
911
 
819
912
  public func discoveryFinished(_ iface: FLIRCommunicationInterface) {
820
- NSLog("[FlirManager] Discovery finished on interface: \(iface)")
913
+ FlirLogger.log(.discovery, "Discovery finished on interface: \(iface)")
821
914
  isScanning = false
822
915
  }
823
916
  }
@@ -826,13 +919,19 @@ extension FlirManager: FLIRDiscoveryEventDelegate {
826
919
 
827
920
  extension FlirManager: FLIRDataReceivedDelegate {
828
921
  public func onDisconnected(_ camera: FLIRCamera, withError error: Error?) {
829
- NSLog("[FlirManager] Camera disconnected: \(error?.localizedDescription ?? "no error")")
922
+ FlirLogger.logError(.disconnect, "Camera disconnected callback", error: error)
923
+
924
+ // Stop battery polling
925
+ stopBatteryPolling()
830
926
 
831
927
  _isConnected = false
832
928
  _isStreaming = false
833
929
  connectedDeviceId = nil
834
930
  connectedDeviceName = nil
835
931
 
932
+ // Reset frame counter
933
+ FlirLogger.resetFrameCounter()
934
+
836
935
  DispatchQueue.main.async { [weak self] in
837
936
  self?.delegate?.onDeviceDisconnected()
838
937
  self?.emitStateChange("disconnected")
@@ -844,7 +943,7 @@ extension FlirManager: FLIRDataReceivedDelegate {
844
943
 
845
944
  extension FlirManager: FLIRStreamDelegate {
846
945
  public func onError(_ error: Error) {
847
- NSLog("[FlirManager] Stream error: \(error)")
946
+ FlirLogger.logError(.streaming, "Stream error", error: error)
848
947
 
849
948
  DispatchQueue.main.async { [weak self] in
850
949
  self?.delegate?.onError("Stream error: \(error.localizedDescription)")
@@ -884,6 +983,9 @@ extension FlirManager: FLIRStreamDelegate {
884
983
  }
885
984
  }
886
985
 
986
+ // Rate-limited frame logging
987
+ FlirLogger.logFrame(width: Int(image.size.width), height: Int(image.size.height), temperature: lastTemperature)
988
+
887
989
  DispatchQueue.main.async { [weak self] in
888
990
  guard let self = self else { return }
889
991
  self.delegate?.onFrameReceived(
@@ -911,7 +1013,7 @@ extension FlirManager: FLIRStreamDelegate {
911
1013
  }
912
1014
  }
913
1015
  } catch {
914
- NSLog("[FlirManager] Streamer update error: \(error)")
1016
+ FlirLogger.logError(.frame, "Streamer update error", error: error)
915
1017
  }
916
1018
  }
917
1019
  }
@@ -136,8 +136,12 @@ RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
136
136
 
137
137
  + (void)emitBatteryUpdateWithLevel:(NSInteger)level charging:(BOOL)charging {
138
138
  NSDictionary *payload = @{@"level" : @(level), @"isCharging" : @(charging)};
139
- [[FlirEventEmitter shared] sendDeviceEvent:@"FlirBatteryUpdated"
140
- body:payload];
139
+ NSLog(@"[FlirModule] Emitting battery update - level: %ld, charging: %d", (long)level, charging);
140
+
141
+ // Note: This is a class method, so we need to get the module instance
142
+ // For now, we'll just log - in production you'd need to get the module instance
143
+ // or convert this to an instance method
144
+ // [[FlirModule sharedInstance] sendEventWithName:@"FlirBatteryUpdated" body:payload];
141
145
  }
142
146
 
143
147
  #pragma mark - Methods
@@ -405,9 +409,10 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
405
409
  objc_msgSend)(d, sel_registerName("toDictionary"))];
406
410
  }
407
411
  }
408
- [[FlirEventEmitter shared]
409
- sendDeviceEvent:@"FlirDevicesFound"
410
- body:@{@"devices" : arr, @"count" : @(arr.count)}];
412
+
413
+ NSLog(@"[FlirModule] onDevicesFound - emitting FlirDevicesFound with %lu devices", (unsigned long)arr.count);
414
+ [self sendEventWithName:@"FlirDevicesFound"
415
+ body:@{@"devices" : arr, @"count" : @(arr.count)}];
411
416
  }
412
417
 
413
418
  - (void)onDeviceConnected:(id)device {
@@ -425,31 +430,31 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
425
430
  device, sel_registerName("toDictionary"))];
426
431
  }
427
432
 
428
- [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:body];
433
+ NSLog(@"[FlirModule] onDeviceConnected - emitting FlirDeviceConnected event");
434
+ [self sendEventWithName:@"FlirDeviceConnected" body:body];
429
435
  }
430
436
 
431
437
  - (void)onDeviceDisconnected {
432
- [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected"
433
- body:@{}];
438
+ NSLog(@"[FlirModule] onDeviceDisconnected - emitting FlirDeviceDisconnected event");
439
+ [self sendEventWithName:@"FlirDeviceDisconnected" body:@{}];
434
440
  }
435
441
 
436
442
  - (void)onFrameReceived:(UIImage *)image
437
443
  width:(NSInteger)width
438
444
  height:(NSInteger)height {
439
445
  // Also emit event for JS consumers (though slow, some might use it)
440
- [[FlirEventEmitter shared]
441
- sendDeviceEvent:@"FlirFrameReceived"
442
- body:@{
443
- @"width" : @(width),
444
- @"height" : @(height),
445
- @"timestamp" :
446
- @([[NSDate date] timeIntervalSince1970] * 1000)
447
- }];
446
+ [self sendEventWithName:@"FlirFrameReceived"
447
+ body:@{
448
+ @"width" : @(width),
449
+ @"height" : @(height),
450
+ @"timestamp" :
451
+ @([[NSDate date] timeIntervalSince1970] * 1000)
452
+ }];
448
453
  }
449
454
 
450
455
  - (void)onFrameReceivedRaw:(NSData *)data width:(NSInteger)width height:(NSInteger)height bytesPerRow:(NSInteger)bytesPerRow timestamp:(double)timestamp {
451
456
  // Emit a lightweight event to notify JS that a raw bitmap is available; raw bytes are available via getLatestFrameBitmap()
452
- [[FlirEventEmitter shared] sendDeviceEvent:@"FlirFrameBitmapAvailable" body:@{
457
+ [self sendEventWithName:@"FlirFrameBitmapAvailable" body:@{
453
458
  @"width": @(width),
454
459
  @"height": @(height),
455
460
  @"bytesPerRow": @(bytesPerRow),
@@ -463,9 +468,9 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
463
468
  self.connectResolve = nil;
464
469
  self.connectReject = nil;
465
470
  }
466
- [[FlirEventEmitter shared]
467
- sendDeviceEvent:@"FlirError"
468
- body:@{@"error" : message ?: @"Unknown error"}];
471
+ NSLog(@"[FlirModule] onError - emitting FlirError: %@", message);
472
+ [self sendEventWithName:@"FlirError"
473
+ body:@{@"error" : message ?: @"Unknown error"}];
469
474
  }
470
475
 
471
476
  - (void)onStateChanged:(NSString *)state
@@ -478,7 +483,8 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
478
483
  @"isStreaming" : @(isStreaming),
479
484
  @"isEmulator" : @(isEmulator)
480
485
  };
481
- [[FlirEventEmitter shared] sendDeviceEvent:@"FlirStateChanged" body:body];
486
+ NSLog(@"[FlirModule] onStateChanged - state: %@, connected: %d, streaming: %d", state, isConnected, isStreaming);
487
+ [self sendEventWithName:@"FlirStateChanged" body:body];
482
488
  }
483
489
 
484
490
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilabs-flir",
3
- "version": "2.1.38",
3
+ "version": "2.1.401",
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",