ilabs-flir 2.3.1 → 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.
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +84 -11
- package/android/Flir/src/main/java/flir/android/FlirModule.kt +44 -0
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +16 -2
- package/ios/Flir/src/FlirManager.swift +66 -2
- package/ios/Flir/src/FlirModule.m +24 -0
- package/package.json +3 -3
- package/src/index.d.ts +6 -0
|
@@ -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 =
|
|
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>
|
|
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
|
/**
|
|
@@ -241,8 +255,8 @@ object FlirManager {
|
|
|
241
255
|
/**
|
|
242
256
|
* Capture a high-fidelity radiometric snapshot (saves thermal data)
|
|
243
257
|
*/
|
|
244
|
-
fun captureRadiometricSnapshot(path: String) {
|
|
245
|
-
sdkManager?.captureRadiometricSnapshot(path)
|
|
258
|
+
fun captureRadiometricSnapshot(path: String, callback: FlirSdkManager.SnapshotCallback? = null) {
|
|
259
|
+
sdkManager?.captureRadiometricSnapshot(path, callback)
|
|
246
260
|
}
|
|
247
261
|
|
|
248
262
|
/**
|
|
@@ -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
|
|
442
|
-
val
|
|
443
|
-
val
|
|
444
|
-
|
|
445
|
-
|
|
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 {
|
|
@@ -257,6 +287,20 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
|
|
|
257
287
|
}
|
|
258
288
|
}
|
|
259
289
|
|
|
290
|
+
@ReactMethod
|
|
291
|
+
fun setSaveRadiometric(enabled: Boolean, promise: Promise?) {
|
|
292
|
+
try {
|
|
293
|
+
val varClass = Class.forName("ilabs.libs.io.data.Var")
|
|
294
|
+
val field = varClass.getField("saveRadiometric")
|
|
295
|
+
field.set(null, enabled)
|
|
296
|
+
Log.d(TAG, "Updated Var.saveRadiometric to $enabled via reflection")
|
|
297
|
+
promise?.resolve(true)
|
|
298
|
+
} catch (e: Exception) {
|
|
299
|
+
Log.w(TAG, "Could not update Var.saveRadiometric via reflection: ${e.message}")
|
|
300
|
+
promise?.reject("ERR_FLIR_SAVE_RADIOMETRIC", e)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
260
304
|
@ReactMethod
|
|
261
305
|
fun updateAcol(value: Float, promise: Promise?) {
|
|
262
306
|
try {
|
|
@@ -73,6 +73,13 @@ public class FlirSdkManager {
|
|
|
73
73
|
void onError(String message);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
public interface SnapshotCallback {
|
|
77
|
+
void onSnapshotSaved(String path);
|
|
78
|
+
void onSnapshotError(String message);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private SnapshotCallback snapshotCallback;
|
|
82
|
+
|
|
76
83
|
private FlirSdkManager(Context context) {
|
|
77
84
|
this.context = context.getApplicationContext();
|
|
78
85
|
}
|
|
@@ -364,13 +371,19 @@ public class FlirSdkManager {
|
|
|
364
371
|
try {
|
|
365
372
|
thermalImage.saveAs(snapshotPath);
|
|
366
373
|
Log.i(TAG, "Radiometric snapshot saved to: " + snapshotPath);
|
|
374
|
+
if (snapshotCallback != null) {
|
|
375
|
+
snapshotCallback.onSnapshotSaved(snapshotPath);
|
|
376
|
+
}
|
|
367
377
|
} catch (java.io.IOException e) {
|
|
368
378
|
Log.e(TAG, "Failed to save radiometric snapshot", e);
|
|
379
|
+
if (snapshotCallback != null) {
|
|
380
|
+
snapshotCallback.onSnapshotError(e.getMessage());
|
|
381
|
+
}
|
|
369
382
|
}
|
|
370
383
|
}
|
|
371
384
|
|
|
372
385
|
// 3. Generate Bitmap for display
|
|
373
|
-
Bitmap bitmap = BitmapAndroid.createBitmap(
|
|
386
|
+
Bitmap bitmap = BitmapAndroid.createBitmap(streamer.getImage()).getBitMap();
|
|
374
387
|
if (bitmap != null) {
|
|
375
388
|
latestBitmap = bitmap;
|
|
376
389
|
if (listener != null) {
|
|
@@ -462,8 +475,9 @@ public class FlirSdkManager {
|
|
|
462
475
|
Log.d(TAG, "Requested palette: " + paletteName);
|
|
463
476
|
}
|
|
464
477
|
|
|
465
|
-
public void captureRadiometricSnapshot(String path) {
|
|
478
|
+
public void captureRadiometricSnapshot(String path, SnapshotCallback callback) {
|
|
466
479
|
this.pendingSnapshotPath = path;
|
|
480
|
+
this.snapshotCallback = callback;
|
|
467
481
|
Log.d(TAG, "Pending radiometric snapshot: " + path);
|
|
468
482
|
}
|
|
469
483
|
|
|
@@ -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
|
|
519
|
-
let palettes =
|
|
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.
|
|
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",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"adm-zip": "^0.5.10"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"typescript": "^5.0.0",
|
|
59
58
|
"@types/react": "*",
|
|
60
|
-
"@types/react-native": "*"
|
|
59
|
+
"@types/react-native": "*",
|
|
60
|
+
"typescript": "^5.0.0"
|
|
61
61
|
},
|
|
62
62
|
"react-native": {
|
|
63
63
|
"ios": {
|
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>;
|