ilabs-flir 2.3.2 → 2.3.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.
@@ -10,6 +10,7 @@ import com.facebook.react.bridge.WritableMap
10
10
  import com.facebook.react.modules.core.DeviceEventManagerModule
11
11
  import com.facebook.react.uimanager.ThemedReactContext
12
12
  import com.flir.thermalsdk.live.Identity
13
+ import com.flir.thermalsdk.image.PaletteManager
13
14
  import java.io.File
14
15
  import java.io.FileOutputStream
15
16
  import java.util.concurrent.atomic.AtomicLong
@@ -75,16 +76,20 @@ object FlirManager {
75
76
  sdkManager?.setPalette(name)
76
77
  // Also try to update the app's global Var.cool if possible
77
78
  try {
78
- val palettes = listOf("iron", "rainbow", "grayscale", "arctic", "lava", "contrast", "hotcold", "medical")
79
+ val palettes = getAvailablePalettes()
79
80
  val idx = palettes.indexOf(name.lowercase())
80
81
  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
82
  updateAcol(idx.toFloat())
84
83
  }
85
84
  } catch (e: Exception) {}
86
85
  }
87
- fun getAvailablePalettes(): List<String> = emptyList()
86
+ fun getAvailablePalettes(): List<String> {
87
+ return try {
88
+ PaletteManager.getDefaultPalettes().map { it.name }
89
+ } catch (e: Exception) {
90
+ listOf("grayscale", "iron", "rainbow", "arctic", "lava", "contrast", "hotcold", "medical")
91
+ }
92
+ }
88
93
 
89
94
  /**
90
95
  * Initialize the FLIR SDK
@@ -102,6 +107,15 @@ object FlirManager {
102
107
 
103
108
  isInitialized = true
104
109
  Log.i(TAG, "FlirManager initialized")
110
+
111
+ // Generate palette icons on background thread
112
+ Thread {
113
+ try {
114
+ generatePaletteIcons(context)
115
+ } catch (e: Exception) {
116
+ Log.e(TAG, "Initial palette icon generation failed", e)
117
+ }
118
+ }.start()
105
119
  }
106
120
 
107
121
  /**
@@ -438,15 +452,74 @@ object FlirManager {
438
452
  coolField.set(null, shaderIdx)
439
453
  Log.d(TAG, "Updated Var.cool to $shaderIdx via reflection (raw=$rawIdx)")
440
454
 
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])
455
+ // If we are in FLIR mode, also notify the SDK palette using the official SDK palette list
456
+ val sdkPalettes = PaletteManager.getDefaultPalettes()
457
+ val paletteNames = sdkPalettes.map { it.name }
458
+ Log.d(TAG, "True FLIR Palettes: $paletteNames")
459
+
460
+ val maxEff = sdkPalettes.size
461
+ val paletteIdx = rawIdx % maxEff
462
+ val safeIdx = if (paletteIdx < 0) paletteIdx + maxEff else paletteIdx
463
+
464
+ val targetPaletteName = paletteNames[safeIdx]
465
+ Log.d(TAG, "Applying true FLIR palette: $targetPaletteName (index: $safeIdx, max: $maxEff)")
466
+
467
+ sdkManager?.setPalette(targetPaletteName)
446
468
 
447
469
  } catch (e: Exception) {
448
470
  Log.w(TAG, "Could not update Var.cool via reflection: ${e.message}")
449
471
  }
450
472
  }
473
+
474
+ /**
475
+ * Generate icons for all default palettes and save to cache.
476
+ */
477
+ fun generatePaletteIcons(context: Context): List<Map<String, String>> {
478
+ val results = mutableListOf<Map<String, String>>()
479
+ try {
480
+ val palettes = PaletteManager.getDefaultPalettes()
481
+ val dir = File(context.cacheDir, "flir_palettes")
482
+ if (!dir.exists()) dir.mkdirs()
483
+
484
+ for (palette in palettes) {
485
+ val iconFile = File(dir, "${palette.name.lowercase()}.png")
486
+ if (!iconFile.exists()) {
487
+ try {
488
+ // Common method names for palette preview in FLIR SDK: getBitmap(), getIcon()
489
+ val bitmap = try {
490
+ val m = palette.javaClass.getMethod("getBitmap")
491
+ m.invoke(palette) as? Bitmap
492
+ } catch (e: Exception) {
493
+ try {
494
+ val m = palette.javaClass.getMethod("getIcon")
495
+ m.invoke(palette) as? Bitmap
496
+ } catch (e2: Exception) { null }
497
+ }
498
+
499
+ bitmap?.let {
500
+ FileOutputStream(iconFile).use { out ->
501
+ it.compress(Bitmap.CompressFormat.PNG, 100, out)
502
+ }
503
+ }
504
+ } catch (e: Exception) {
505
+ Log.w(TAG, "Failed to save icon for ${palette.name}: ${e.message}")
506
+ }
507
+ }
508
+ results.add(mapOf(
509
+ "name" to palette.name,
510
+ "uri" to if (iconFile.exists()) "file://${iconFile.absolutePath}" else ""
511
+ ))
512
+ }
513
+ } catch (e: Exception) {
514
+ Log.e(TAG, "Palette icon generation error", e)
515
+ }
516
+ return results
517
+ }
518
+
519
+ fun getPalettesWithIcons(context: Context? = null): List<Map<String, String>> {
520
+ val ctx = context ?: reactContext ?: return emptyList()
521
+ return generatePaletteIcons(ctx)
522
+ }
523
+
451
524
  fun destroy() { stop() }
452
525
  }
@@ -137,6 +137,36 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
137
137
  }
138
138
  }
139
139
 
140
+ @ReactMethod
141
+ fun getAvailablePalettes(promise: Promise?) {
142
+ try {
143
+ val palettes = FlirManager.getAvailablePalettes()
144
+ val result = com.facebook.react.bridge.Arguments.createArray()
145
+ palettes.forEach { result.pushString(it) }
146
+ promise?.resolve(result)
147
+ } catch (e: Exception) {
148
+ promise?.reject("ERR_FLIR_PALETTES", e)
149
+ }
150
+ }
151
+
152
+ @ReactMethod
153
+ fun getPalettesWithIcons(promise: Promise?) {
154
+ try {
155
+ val palettes = FlirManager.getPalettesWithIcons(reactContext)
156
+ val result = com.facebook.react.bridge.Arguments.createArray()
157
+
158
+ palettes.forEach { p ->
159
+ val map = com.facebook.react.bridge.Arguments.createMap()
160
+ map.putString("name", p["name"])
161
+ map.putString("uri", p["uri"])
162
+ result.pushMap(map)
163
+ }
164
+ promise?.resolve(result)
165
+ } catch (e: Exception) {
166
+ promise?.reject("ERR_FLIR_PALETTE_ICONS", e)
167
+ }
168
+ }
169
+
140
170
  @ReactMethod
141
171
  fun getDiscoveredDevices(promise: Promise?) {
142
172
  try {
@@ -91,6 +91,11 @@ import ThermalSDK
91
91
  private override init() {
92
92
  super.init()
93
93
  NSLog("[FlirManager] Initialized")
94
+
95
+ // Generate palette icons on background thread
96
+ DispatchQueue.global().async {
97
+ self.generatePaletteIcons()
98
+ }
94
99
  }
95
100
 
96
101
  // MARK: - Public State
@@ -515,14 +520,73 @@ import ThermalSDK
515
520
  }
516
521
 
517
522
  @objc public func setPaletteFromAcol(_ acol: Float) {
518
- // Map acol (0..7) to palette names
519
- let palettes = ["iron", "rainbow", "grayscale", "arctic", "lava", "contrast", "hotcold", "medical"]
523
+ // Map acol to dynamic palette names
524
+ let palettes = getAvailablePalettes()
520
525
  let idx = Int(acol)
521
526
  if idx >= 0 && idx < palettes.count {
522
527
  setPalette(palettes[idx])
523
528
  }
524
529
  }
525
530
 
531
+ @objc public func getAvailablePalettes() -> [String] {
532
+ #if FLIR_ENABLED
533
+ // We use a dummy thermal image to access palette manager if no streamer exists,
534
+ // but typically PaletteManager.getDefaultPalettes() might be static or available via streamer
535
+ if let streamer = streamer {
536
+ var names: [String] = []
537
+ streamer.withThermalImage { img in
538
+ names = img.paletteManager.getDefaultPalettes().map { $0.name }
539
+ }
540
+ if !names.isEmpty { return names }
541
+ }
542
+
543
+ // Fallback: Try to get from a temporary thermal image if possible,
544
+ // but FLIR SDK often requires a valid image.
545
+ // For now, return a standard list if we can't get it dynamically,
546
+ // or try to find a static way.
547
+ #endif
548
+ return ["iron", "rainbow", "grayscale", "arctic", "lava", "contrast", "hotcold", "medical"]
549
+ }
550
+
551
+ @objc public func generatePaletteIcons() -> [[String: String]] {
552
+ var results: [[String: String]] = []
553
+ #if FLIR_ENABLED
554
+ guard let streamer = streamer else { return [] }
555
+
556
+ let fileManager = FileManager.default
557
+ let cacheDir = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
558
+ let paletteDir = cacheDir.appendingPathComponent("flir_palettes")
559
+
560
+ if !fileManager.fileExists(atPath: paletteDir.path) {
561
+ try? fileManager.createDirectory(at: paletteDir, withIntermediateDirectories: true)
562
+ }
563
+
564
+ streamer.withThermalImage { thermalImage in
565
+ let palettes = thermalImage.paletteManager.getDefaultPalettes()
566
+ for palette in palettes {
567
+ let iconURL = paletteDir.appendingPathComponent("\(palette.name.lowercased()).png")
568
+ if !fileManager.fileExists(atPath: iconURL.path) {
569
+ // Assuming palette.icon exists (common in FLIR SDK)
570
+ if let icon = palette.icon {
571
+ if let data = icon.pngData() {
572
+ try? data.write(to: iconURL)
573
+ }
574
+ }
575
+ }
576
+ results.append([
577
+ "name": palette.name,
578
+ "uri": fileManager.fileExists(atPath: iconURL.path) ? iconURL.absoluteString : ""
579
+ ])
580
+ }
581
+ }
582
+ #endif
583
+ return results
584
+ }
585
+
586
+ @objc public func getPalettesWithIcons() -> [[String: String]] {
587
+ return generatePaletteIcons()
588
+ }
589
+
526
590
  @objc public func captureRadiometricSnapshot(_ path: String) {
527
591
  self.pendingSnapshotPath = path
528
592
  NSLog("[FlirManager] Pending radiometric snapshot: \(path)")
@@ -466,6 +466,30 @@ RCT_EXPORT_METHOD(isBatteryCharging : (RCTPromiseResolveBlock)
466
466
  });
467
467
  }
468
468
 
469
+ RCT_EXPORT_METHOD(getAvailablePalettes : (RCTPromiseResolveBlock)
470
+ resolve rejecter : (RCTPromiseRejectBlock)reject) {
471
+ dispatch_async(dispatch_get_main_queue(), ^{
472
+ id manager = flir_manager_shared();
473
+ NSArray *palettes = @[];
474
+ if (manager && [manager respondsToSelector:sel_registerName("getAvailablePalettes")]) {
475
+ palettes = ((NSArray * (*)(id, SEL)) objc_msgSend)(manager, sel_registerName("getAvailablePalettes"));
476
+ }
477
+ if (resolve) resolve(palettes);
478
+ });
479
+ }
480
+
481
+ RCT_EXPORT_METHOD(getPalettesWithIcons : (RCTPromiseResolveBlock)
482
+ resolve rejecter : (RCTPromiseRejectBlock)reject) {
483
+ dispatch_async(dispatch_get_main_queue(), ^{
484
+ id manager = flir_manager_shared();
485
+ NSArray *palettes = @[];
486
+ if (manager && [manager respondsToSelector:sel_registerName("getPalettesWithIcons")]) {
487
+ palettes = ((NSArray * (*)(id, SEL)) objc_msgSend)(manager, sel_registerName("getPalettesWithIcons"));
488
+ }
489
+ if (resolve) resolve(palettes);
490
+ });
491
+ }
492
+
469
493
  RCT_EXPORT_METHOD(setPreferSdkRotation : (BOOL)prefer resolver : (
470
494
  RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
471
495
  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.2",
3
+ "version": "2.3.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",
package/src/index.d.ts CHANGED
@@ -54,6 +54,12 @@ export interface FlirModuleAPI {
54
54
  stopFlir(): Promise<boolean>;
55
55
  getDiscoveredDevices(): Promise<FlirDevice[]>;
56
56
 
57
+ // Palette & Imaging APIs
58
+ getAvailablePalettes(): Promise<string[]>;
59
+ getPalettesWithIcons(): Promise<{ name: string; uri: string }[]>;
60
+ setPalette(name: string): Promise<boolean>;
61
+ captureRadiometricSnapshot(path: string): Promise<boolean>;
62
+
57
63
  // Debug APIs
58
64
  initializeSDK(): Promise<SDKInitResult>;
59
65
  getDebugInfo(): Promise<FlirDebugInfo>;