ilabs-flir 2.3.0 → 2.3.1

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.
@@ -71,7 +71,19 @@ object FlirManager {
71
71
  fun isPreferSdkRotation(): Boolean = false
72
72
  fun getBatteryLevel(): Int = -1
73
73
  fun isBatteryCharging(): Boolean = false
74
- fun setPalette(name: String) { /* No-op */ }
74
+ fun setPalette(name: String) {
75
+ sdkManager?.setPalette(name)
76
+ // Also try to update the app's global Var.cool if possible
77
+ try {
78
+ val palettes = listOf("iron", "rainbow", "grayscale", "arctic", "lava", "contrast", "hotcold", "medical")
79
+ val idx = palettes.indexOf(name.lowercase())
80
+ if (idx != -1) {
81
+ // If we found the index, we use the raw value.
82
+ // But if the name itself is passed as a number or from a loop, handle it.
83
+ updateAcol(idx.toFloat())
84
+ }
85
+ } catch (e: Exception) {}
86
+ }
75
87
  fun getAvailablePalettes(): List<String> = emptyList()
76
88
 
77
89
  /**
@@ -227,6 +239,13 @@ object FlirManager {
227
239
  }
228
240
 
229
241
  /**
242
+ * Capture a high-fidelity radiometric snapshot (saves thermal data)
243
+ */
244
+ fun captureRadiometricSnapshot(path: String) {
245
+ sdkManager?.captureRadiometricSnapshot(path)
246
+ }
247
+
248
+ /**
230
249
  * Get latest frame as file path (for RN)
231
250
  */
232
251
  fun getLatestFramePath(): String? {
@@ -401,6 +420,33 @@ object FlirManager {
401
420
  fun enableEmulatorMode() = startDiscovery()
402
421
  fun forceEmulatorMode(type: String = "FLIR_ONE_EDGE") { startDiscovery() }
403
422
  fun setPreferredEmulatorType(type: String) { }
404
- fun updateAcol(value: Float) { }
423
+ fun updateAcol(value: Float) {
424
+ Log.d(TAG, "updateAcol: $value")
425
+ // Use reflection to update ilabs.libs.io.data.Var.cool to avoid circular dependency
426
+ try {
427
+ val varClass = Class.forName("ilabs.libs.io.data.Var")
428
+ val coolField = varClass.getField("cool")
429
+
430
+ // Modular logic for palettes: if index > 7, wrap around (17 mod 8)
431
+ // But we keep shader variants (14, 15, 16) as-is if within that range
432
+ val rawIdx = value.toInt()
433
+ var shaderIdx = rawIdx
434
+ if (shaderIdx > 16) {
435
+ shaderIdx = shaderIdx % 16 // Shader loop
436
+ }
437
+
438
+ coolField.set(null, shaderIdx)
439
+ Log.d(TAG, "Updated Var.cool to $shaderIdx via reflection (raw=$rawIdx)")
440
+
441
+ // If we are in FLIR mode, also notify the SDK palette with modular loop
442
+ val palettes = listOf("iron", "rainbow", "grayscale", "arctic", "lava", "contrast", "hotcold", "medical")
443
+ val paletteIdx = rawIdx % palettes.size
444
+ val safeIdx = if (paletteIdx < 0) paletteIdx + palettes.size else paletteIdx
445
+ sdkManager?.setPalette(palettes[safeIdx])
446
+
447
+ } catch (e: Exception) {
448
+ Log.w(TAG, "Could not update Var.cool via reflection: ${e.message}")
449
+ }
450
+ }
405
451
  fun destroy() { stop() }
406
452
  }
@@ -256,6 +256,28 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
256
256
  promise?.reject("ERR_FLIR_STOP", e)
257
257
  }
258
258
  }
259
+
260
+ @ReactMethod
261
+ fun updateAcol(value: Float, promise: Promise?) {
262
+ try {
263
+ FlirManager.updateAcol(value)
264
+ promise?.resolve(true)
265
+ } catch (e: Exception) {
266
+ promise?.reject("ERR_FLIR_ACOL", e)
267
+ }
268
+ }
269
+
270
+ @ReactMethod
271
+ fun setPalette(name: String?, promise: Promise?) {
272
+ try {
273
+ if (name != null) {
274
+ FlirManager.setPalette(name)
275
+ }
276
+ promise?.resolve(true)
277
+ } catch (e: Exception) {
278
+ promise?.reject("ERR_FLIR_PALETTE", e)
279
+ }
280
+ }
259
281
 
260
282
  @ReactMethod
261
283
  fun initializeSDK(promise: Promise?) {
@@ -8,7 +8,10 @@ import android.util.Log;
8
8
  import com.flir.thermalsdk.ErrorCode;
9
9
  import com.flir.thermalsdk.androidsdk.ThermalSdkAndroid;
10
10
  import com.flir.thermalsdk.androidsdk.image.BitmapAndroid;
11
+ import com.flir.thermalsdk.image.Palette;
12
+ import com.flir.thermalsdk.image.PaletteManager;
11
13
  import com.flir.thermalsdk.image.Point;
14
+ import com.flir.thermalsdk.image.ThermalImage;
12
15
  import com.flir.thermalsdk.image.ThermalValue;
13
16
  import com.flir.thermalsdk.live.AuthenticationResponse;
14
17
  import com.flir.thermalsdk.live.Camera;
@@ -48,8 +51,10 @@ public class FlirSdkManager {
48
51
  private Stream activeStream;
49
52
  private final List<Identity> discoveredDevices = Collections.synchronizedList(new ArrayList<>());
50
53
  private volatile Bitmap latestBitmap;
54
+ private volatile String currentPaletteName = "iron";
51
55
  private final AtomicBoolean isProcessingFrame = new AtomicBoolean(false);
52
56
  private boolean useHalfScale = false;
57
+ private String pendingSnapshotPath = null;
53
58
 
54
59
  // Listener
55
60
  private Listener listener;
@@ -337,14 +342,42 @@ public class FlirSdkManager {
337
342
  if (streamer != null && activeStream != null) {
338
343
  streamer.update();
339
344
 
340
- // createBitmap is safe to do here or in executor
341
- Bitmap bitmap = BitmapAndroid.createBitmap(streamer.getImage()).getBitMap();
342
- if (bitmap != null) {
343
- latestBitmap = bitmap;
344
- if (listener != null) {
345
- listener.onFrame(bitmap);
345
+ final String paletteToApply = currentPaletteName;
346
+ final String snapshotPath = pendingSnapshotPath;
347
+ pendingSnapshotPath = null;
348
+
349
+ streamer.withThermalImage(thermalImage -> {
350
+ // 1. Apply Palette
351
+ if (paletteToApply != null) {
352
+ Palette palette =
353
+ PaletteManager.getDefaultPalettes().stream()
354
+ .filter(p -> p.name.equalsIgnoreCase(paletteToApply))
355
+ .findFirst()
356
+ .orElse(null);
357
+ if (palette != null) {
358
+ thermalImage.setPalette(palette);
359
+ }
346
360
  }
347
- }
361
+
362
+ // 2. Save Radiometric Snapshot if requested
363
+ if (snapshotPath != null) {
364
+ try {
365
+ thermalImage.saveAs(snapshotPath);
366
+ Log.i(TAG, "Radiometric snapshot saved to: " + snapshotPath);
367
+ } catch (java.io.IOException e) {
368
+ Log.e(TAG, "Failed to save radiometric snapshot", e);
369
+ }
370
+ }
371
+
372
+ // 3. Generate Bitmap for display
373
+ Bitmap bitmap = BitmapAndroid.createBitmap(thermalImage.getImage()).getBitMap();
374
+ if (bitmap != null) {
375
+ latestBitmap = bitmap;
376
+ if (listener != null) {
377
+ listener.onFrame(bitmap);
378
+ }
379
+ }
380
+ });
348
381
  }
349
382
  } catch (Exception e) {
350
383
  Log.e(TAG, "Frame processing error", e);
@@ -423,6 +456,16 @@ public class FlirSdkManager {
423
456
  }
424
457
 
425
458
  // ==================== LISTENERS ====================
459
+
460
+ public void setPalette(String paletteName) {
461
+ this.currentPaletteName = paletteName;
462
+ Log.d(TAG, "Requested palette: " + paletteName);
463
+ }
464
+
465
+ public void captureRadiometricSnapshot(String path) {
466
+ this.pendingSnapshotPath = path;
467
+ Log.d(TAG, "Pending radiometric snapshot: " + path);
468
+ }
426
469
 
427
470
  private final DiscoveryEventListener discoveryListener = new DiscoveryEventListener() {
428
471
  @Override
@@ -66,6 +66,10 @@ import ThermalSDK
66
66
  // Internal synchronization
67
67
  private let stateLock = NSObject()
68
68
 
69
+ // Palette and Snapshot state
70
+ private var currentPaletteName: String = "iron"
71
+ private var pendingSnapshotPath: String?
72
+
69
73
  // Dedicated render queue for frame processing (matches sample app pattern)
70
74
  private let renderQueue = DispatchQueue(label: "com.flir.render")
71
75
 
@@ -506,11 +510,22 @@ import ThermalSDK
506
510
  }
507
511
 
508
512
  @objc public func setPalette(_ name: String) {
509
- // stub
513
+ self.currentPaletteName = name
514
+ NSLog("[FlirManager] Requested palette: \(name)")
510
515
  }
511
516
 
512
517
  @objc public func setPaletteFromAcol(_ acol: Float) {
513
- // stub
518
+ // Map acol (0..7) to palette names
519
+ let palettes = ["iron", "rainbow", "grayscale", "arctic", "lava", "contrast", "hotcold", "medical"]
520
+ let idx = Int(acol)
521
+ if idx >= 0 && idx < palettes.count {
522
+ setPalette(palettes[idx])
523
+ }
524
+ }
525
+
526
+ @objc public func captureRadiometricSnapshot(_ path: String) {
527
+ self.pendingSnapshotPath = path
528
+ NSLog("[FlirManager] Pending radiometric snapshot: \(path)")
514
529
  }
515
530
 
516
531
  @objc public func retainClient(_ clientId: String) {
@@ -684,8 +699,30 @@ extension FlirManager: FLIRStreamDelegate {
684
699
  return
685
700
  }
686
701
 
702
+ let paletteToApply = self.currentPaletteName
703
+ let snapshotPath = self.pendingSnapshotPath
704
+ self.pendingSnapshotPath = nil
705
+
687
706
  do {
688
707
  try streamer.update()
708
+
709
+ streamer.withThermalImage { thermalImage in
710
+ // 1. Apply Palette
711
+ if let palette = thermalImage.paletteManager.getDefaultPalettes().first(where: { $0.name.lowercased() == paletteToApply.lowercased() }) {
712
+ thermalImage.palette = palette
713
+ }
714
+
715
+ // 2. Save Radiometric Snapshot if requested
716
+ if let path = snapshotPath {
717
+ do {
718
+ try thermalImage.save(to: path)
719
+ NSLog("[FlirManager] Radiometric snapshot saved to: \(path)")
720
+ } catch {
721
+ NSLog("[FlirManager] Failed to save radiometric snapshot: \(error)")
722
+ }
723
+ }
724
+ }
725
+
689
726
  NSLog("[FLIR-TRACE 3️⃣] Streamer updated successfully")
690
727
  } catch {
691
728
  NSLog("[FLIR-TRACE ❌] Streamer update failed: \(error)")
@@ -479,6 +479,34 @@ RCT_EXPORT_METHOD(setPreferSdkRotation : (BOOL)prefer resolver : (
479
479
  });
480
480
  }
481
481
 
482
+ RCT_EXPORT_METHOD(setPalette : (NSString *)name resolver : (RCTPromiseResolveBlock)
483
+ resolve rejecter : (RCTPromiseRejectBlock)reject) {
484
+ dispatch_async(dispatch_get_main_queue(), ^{
485
+ id manager = flir_manager_shared();
486
+ if (manager &&
487
+ [manager respondsToSelector:sel_registerName("setPalette:")]) {
488
+ ((void (*)(id, SEL, id))objc_msgSend)(manager,
489
+ sel_registerName("setPalette:"),
490
+ name);
491
+ }
492
+ if (resolve) resolve(@(YES));
493
+ });
494
+ }
495
+
496
+ RCT_EXPORT_METHOD(captureRadiometricSnapshot : (NSString *)path resolver : (RCTPromiseResolveBlock)
497
+ resolve rejecter : (RCTPromiseRejectBlock)reject) {
498
+ dispatch_async(dispatch_get_main_queue(), ^{
499
+ id manager = flir_manager_shared();
500
+ if (manager &&
501
+ [manager respondsToSelector:sel_registerName("captureRadiometricSnapshot:")]) {
502
+ ((void (*)(id, SEL, id))objc_msgSend)(manager,
503
+ sel_registerName("captureRadiometricSnapshot:"),
504
+ path);
505
+ }
506
+ if (resolve) resolve(@(YES));
507
+ });
508
+ }
509
+
482
510
  RCT_EXPORT_METHOD(isPreferSdkRotation : (RCTPromiseResolveBlock)
483
511
  resolve rejecter : (RCTPromiseRejectBlock)reject) {
484
512
  dispatch_async(dispatch_get_main_queue(), ^{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilabs-flir",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
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",