ilabs-flir 2.1.402 → 2.2.2

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.
@@ -174,25 +174,35 @@ import ThermalSDK
174
174
  }
175
175
 
176
176
  @objc public func getBatteryLevel() -> Int {
177
- #if FLIR_ENABLED
178
- if let cam = camera {
179
- if let val = cam.value(forKey: "batteryLevel") as? Int { return val }
180
- if let batt = cam.value(forKey: "battery") as? NSObject,
181
- let lv = batt.value(forKey: "level") as? Int { return lv }
177
+ // Emulators don't have battery - return -1 immediately
178
+ if isEmulator {
179
+ return -1
182
180
  }
183
- #endif
181
+
182
+ #if FLIR_ENABLED
183
+ // Only try battery access on real hardware (not emulators)
184
+ // Note: KVC can throw NSException which Swift can't catch
185
+ // So we avoid using KVC entirely for battery
186
+ return -1 // TODO: Find safe API to access battery without KVC
187
+ #else
184
188
  return -1
189
+ #endif
185
190
  }
186
191
 
187
192
  @objc public func isBatteryCharging() -> Bool {
188
- #if FLIR_ENABLED
189
- if let cam = camera {
190
- if let ch = cam.value(forKey: "isCharging") as? Bool { return ch }
191
- if let batt = cam.value(forKey: "battery") as? NSObject,
192
- let ch = batt.value(forKey: "charging") as? Bool { return ch }
193
+ // Emulators don't have battery - return false immediately
194
+ if isEmulator {
195
+ return false
193
196
  }
194
- #endif
197
+
198
+ #if FLIR_ENABLED
199
+ // Only try battery access on real hardware (not emulators)
200
+ // Note: KVC can throw NSException which Swift can't catch
201
+ // So we avoid using KVC entirely for battery
202
+ return false // TODO: Find safe API to access battery without KVC
203
+ #else
195
204
  return false
205
+ #endif
196
206
  }
197
207
 
198
208
  @objc public func latestFrameImage() -> UIImage? {
@@ -509,6 +519,8 @@ import ThermalSDK
509
519
  connectedDeviceName = identity.deviceId()
510
520
  _isConnected = true
511
521
 
522
+ FlirLogger.log(.connection, "[Flir-BRIDGE-CONNECTION] Connected to: \(identity.deviceId())")
523
+
512
524
  // Get camera info if available
513
525
  if let remoteControl = cam.getRemoteControl(),
514
526
  let cameraInfo = try? remoteControl.getCameraInformation() {
@@ -542,8 +554,10 @@ import ThermalSDK
542
554
  communicationType: self.communicationInterfaceName(identity.communicationInterface()),
543
555
  isEmulator: identity.communicationInterface() == .emulator
544
556
  )
545
- self.delegate?.onDeviceConnected(deviceInfo)
557
+
558
+ // Emit connected state (matches Android emitDeviceState("connected"))
546
559
  self.emitStateChange("connected")
560
+ self.delegate?.onDeviceConnected(deviceInfo)
547
561
  }
548
562
 
549
563
  } catch {
@@ -697,7 +711,10 @@ import ThermalSDK
697
711
  // MARK: - Battery Polling (like Android)
698
712
 
699
713
  private func startBatteryPolling() {
700
- FlirLogger.log(.battery, "Starting battery polling timer (5s interval)")
714
+ // Don't poll battery on emulators - they don't have batteries
715
+ if isEmulator {
716
+ return
717
+ }
701
718
 
702
719
  // Cancel any existing timer
703
720
  stopBatteryPolling()
@@ -713,9 +730,6 @@ import ThermalSDK
713
730
  }
714
731
 
715
732
  private func stopBatteryPolling() {
716
- if batteryPollingTimer != nil {
717
- FlirLogger.log(.battery, "Stopping battery polling timer")
718
- }
719
733
  batteryPollingTimer?.invalidate()
720
734
  batteryPollingTimer = nil
721
735
  }
@@ -724,13 +738,11 @@ import ThermalSDK
724
738
  let level = getBatteryLevel()
725
739
  let charging = isBatteryCharging()
726
740
 
727
- // Only log and emit if values changed
741
+ // Only emit if values changed
728
742
  if level != lastPolledBatteryLevel || charging != lastPolledCharging {
729
743
  lastPolledBatteryLevel = level
730
744
  lastPolledCharging = charging
731
745
 
732
- FlirLogger.logBattery(level: level, isCharging: charging)
733
-
734
746
  // Emit to delegate/RN via notification
735
747
  NotificationCenter.default.post(
736
748
  name: Notification.Name("FlirBatteryUpdated"),
@@ -97,7 +97,9 @@ static BOOL flir_isPreferSdkRotation(void) {
97
97
  @property(nonatomic, copy) RCTPromiseRejectBlock connectReject;
98
98
  @end
99
99
 
100
- @implementation FlirModule
100
+ @implementation FlirModule {
101
+ NSInteger _listenerCount;
102
+ }
101
103
 
102
104
  RCT_EXPORT_MODULE(FlirModule);
103
105
 
@@ -107,6 +109,7 @@ RCT_EXPORT_MODULE(FlirModule);
107
109
 
108
110
  - (instancetype)init {
109
111
  if (self = [super init]) {
112
+ _listenerCount = 0;
110
113
  // Wire up delegate
111
114
  id manager = flir_manager_shared();
112
115
  if (manager) {
@@ -126,7 +129,24 @@ RCT_EXPORT_MODULE(FlirModule);
126
129
  ];
127
130
  }
128
131
 
132
+ - (void)startObserving {
133
+ // Called automatically by RCTEventEmitter when first listener is added
134
+ // This ensures the parent class knows we have listeners
135
+ }
136
+
137
+ - (void)stopObserving {
138
+ // Called automatically by RCTEventEmitter when last listener is removed
139
+ }
140
+
129
141
  RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
142
+ _listenerCount++;
143
+ NSLog(@"[FlirModule] addListener: %@ (count: %ld)", eventName, (long)_listenerCount);
144
+
145
+ // CRITICAL: Call parent to register with RCTEventEmitter's internal tracking
146
+ // Without this, sendEventWithName will show "no listeners registered" warning
147
+ // and may not deliver events properly
148
+ [super addListener:eventName];
149
+
130
150
  // When FlirDevicesFound listener is added, immediately emit current device list
131
151
  // This handles the case where discovery happened before React Native mounted
132
152
  if ([eventName isEqualToString:@"FlirDevicesFound"]) {
@@ -145,7 +165,12 @@ RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
145
165
  }
146
166
 
147
167
  RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
148
- // Required for RCTEventEmitter
168
+ _listenerCount -= count;
169
+ if (_listenerCount < 0) _listenerCount = 0;
170
+ NSLog(@"[FlirModule] removeListeners: %ld (remaining: %ld)", (long)count, (long)_listenerCount);
171
+
172
+ // CRITICAL: Call parent to unregister with RCTEventEmitter's internal tracking
173
+ [super removeListeners:count];
149
174
  }
150
175
 
151
176
  + (void)emitBatteryUpdateWithLevel:(NSInteger)level charging:(BOOL)charging {
@@ -226,18 +251,26 @@ RCT_EXPORT_METHOD(getDiscoveredDevices : (RCTPromiseResolveBlock)
226
251
  RCT_EXPORT_METHOD(connectToDevice : (NSString *)deviceId resolver : (
227
252
  RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
228
253
  dispatch_async(dispatch_get_main_queue(), ^{
229
- self.connectResolve = resolve;
230
- self.connectReject = reject;
254
+ NSLog(@"[FlirModule] connectToDevice called for: %@", deviceId);
231
255
 
232
256
  id manager = flir_manager_shared();
233
257
  if (manager &&
234
258
  [manager respondsToSelector:sel_registerName("connectToDevice:")]) {
259
+ NSLog(@"[FlirModule] Calling FlirManager.connectToDevice");
260
+
261
+ // Store callbacks for event-driven updates (but don't block on them)
262
+ self.connectResolve = nil; // Don't use promise for blocking
263
+ self.connectReject = nil;
264
+
265
+ // Initiate connection asynchronously
235
266
  ((void (*)(id, SEL, id))objc_msgSend)(
236
267
  manager, sel_registerName("connectToDevice:"), deviceId);
268
+
269
+ // Resolve immediately - connection status will come via events
270
+ resolve(@(YES));
237
271
  } else {
272
+ NSLog(@"[FlirModule] FlirManager not found");
238
273
  reject(@"ERR_NO_MANAGER", @"FlirManager not found", nil);
239
- self.connectResolve = nil;
240
- self.connectReject = nil;
241
274
  }
242
275
  });
243
276
  }
@@ -269,16 +302,21 @@ RCT_EXPORT_METHOD(stopFlir : (RCTPromiseResolveBlock)
269
302
  RCT_EXPORT_METHOD(startEmulator : (NSString *)emulatorType resolver : (
270
303
  RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
271
304
  dispatch_async(dispatch_get_main_queue(), ^{
272
- self.connectResolve = resolve;
273
- self.connectReject = reject;
305
+ NSLog(@"[FlirModule] startEmulator called for type: %@", emulatorType);
306
+
274
307
  id manager = flir_manager_shared();
275
308
  if (manager && [manager respondsToSelector:sel_registerName(
276
309
  "startEmulatorWithType:")]) {
277
- // Swift: startEmulator(type: String) -> exposed as startEmulatorWithType:
278
- // ? Or startEmulatorWith? Swift default naming: startEmulator(type:) ->
279
- // startEmulatorWithType:
310
+ // Store callbacks for event-driven updates (but don't block on them)
311
+ self.connectResolve = nil;
312
+ self.connectReject = nil;
313
+
314
+ // Initiate emulator start asynchronously
280
315
  ((void (*)(id, SEL, id))objc_msgSend)(
281
316
  manager, sel_registerName("startEmulatorWithType:"), emulatorType);
317
+
318
+ // Resolve immediately - connection status will come via events
319
+ resolve(@(YES));
282
320
  } else {
283
321
  // Fallback if selector assumption wrong/mismatch
284
322
  reject(@"ERR_NOT_IMPL",
@@ -424,18 +462,18 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
424
462
  }
425
463
  }
426
464
 
427
- NSLog(@"[FlirModule] onDevicesFound - emitting FlirDevicesFound with %lu devices", (unsigned long)arr.count);
428
- [self sendEventWithName:@"FlirDevicesFound"
429
- body:@{@"devices" : arr, @"count" : @(arr.count)}];
465
+ NSLog(@"[FlirModule] onDevicesFound - %lu devices, listenerCount: %ld", (unsigned long)arr.count, (long)_listenerCount);
466
+
467
+ if (_listenerCount > 0) {
468
+ NSLog(@"[FlirModule] emitting FlirDevicesFound event");
469
+ [self sendEventWithName:@"FlirDevicesFound"
470
+ body:@{@"devices" : arr, @"count" : @(arr.count)}];
471
+ } else {
472
+ NSLog(@"[FlirModule] ⚠️ No listeners registered yet - devices will be re-emitted when listener is added");
473
+ }
430
474
  }
431
475
 
432
476
  - (void)onDeviceConnected:(id)device {
433
- if (self.connectResolve) {
434
- self.connectResolve(@(YES));
435
- self.connectResolve = nil;
436
- self.connectReject = nil;
437
- }
438
-
439
477
  // device is FlirDeviceInfo
440
478
  NSMutableDictionary *body = [NSMutableDictionary new];
441
479
  if ([device respondsToSelector:sel_registerName("toDictionary")]) {
@@ -483,11 +521,6 @@ RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
483
521
  }
484
522
 
485
523
  - (void)onError:(NSString *)message {
486
- if (self.connectReject) {
487
- self.connectReject(@"ERR_FLIR", message, nil);
488
- self.connectResolve = nil;
489
- self.connectReject = nil;
490
- }
491
524
  NSLog(@"[FlirModule] onError - emitting FlirError: %@", message);
492
525
  [self sendEventWithName:@"FlirError"
493
526
  body:@{@"error" : message ?: @"Unknown error"}];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilabs-flir",
3
- "version": "2.1.402",
3
+ "version": "2.2.2",
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",