ilabs-flir 2.3.12 → 2.4.0

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/README.md CHANGED
@@ -1,1068 +1,99 @@
1
- #### Best Practices
2
-
3
- Since the FLIR SDK is compiled into this package at build time, you generally do not need to manage runtime downloads. The following guidelines help when including or updating SDK artifacts:
4
-
5
- 1. Verify that the SDK artifacts are present in the package (`android/Flir/libs/` and `ios/Flir/Frameworks/`) before building your app.
6
- 2. For development builds without a vendor SDK, set `FLIR_DISABLED=1` or compile without `FLIR_ENABLED` to use stubbed behavior.
7
- 3. When updating vendor binaries, follow the vendor release and copy the updated AAR/XCFramework files in place before building.
8
- <!-- Runtime SDK download examples removed. The FLIR SDK is included at compile-time; see 'Bundled SDK' above. -->
9
-
10
- #### Bundled SDK: Where to get and how to update vendor binaries
11
-
12
- The FLIR SDK native binaries (Android AAR and iOS XCFramework) are included in this package at compile time under:
13
-
14
- - Android: `android/Flir/libs/` (AAR files)
15
- - iOS: `ios/Flir/Frameworks/` (XCFrameworks and related dylibs)
16
-
17
-
18
- If you need to update the vendor binaries (for example, to a newer FLIR SDK release), download the official release artifacts or rely on the built-in postinstall hook which downloads the binary zips and extracts the native files into the module folders.
19
-
20
- - Official artifacts: https://github.com/PraveenOjha/flir-sdk-binaries/releases
21
- - Android archive (zip): https://github.com/PraveenOjha/flir-sdk-binaries/releases/download/v1.0.1/android.zip
22
- - iOS archive (zip): https://github.com/PraveenOjha/flir-sdk-binaries/releases/download/v1.0.1/ios.zip
23
-
24
- Automated download on package install
25
- ------------------------------------
26
-
27
- This package includes a `postinstall` script which attempts to download the vendor zip archives and extract the artifacts when you run `npm install`. The hook will:
28
-
29
- - Download `android.zip` and extract `.aar` files into `android/Flir/libs/`
30
- - Download `ios.zip` and extract `.xcframework`/`.dylib`/`.framework` files into `ios/Flir/Frameworks/`
31
-
32
- Important notes:
33
- - The installer expects both `android/Flir/libs/` and `ios/Flir/Frameworks/` to already exist inside the installed package — it will NOT create those directories and will fail if they are missing. Do NOT delete those directories from the package.
34
- - To skip automated download for CI or manual installs, set `FLIR_SDK_SKIP_DOWNLOAD=1` before `npm install`.
35
- - If you prefer not to use the postinstall hook, you can manually download and extract the release zips into the same folders.
36
-
37
- After extracting the archives, ensure the following files are in the right place:
38
-
39
- - `android/Flir/libs/thermalsdk-release.aar`
40
- - `android/Flir/libs/androidsdk-release.aar`
41
- - `ios/Flir/Frameworks/ThermalSDK.xcframework` and related frameworks/dylibs
42
-
43
- Build the app normally. There are no runtime downloads performed — the SDK is packaged with the module.
44
-
45
- ### Advanced: Google Play Feature Delivery (Android)
46
-
47
- For production Android apps distributed via Google Play Store, you can use **Play Feature Delivery** to have Google host and serve the SDK binaries at zero cost.
48
-
49
- **Benefits:**
50
- - Zero hosting and bandwidth costs (Google serves the files)
51
- - ✅ Faster downloads via Google's CDN
52
- - ✅ Automatic updates with your app
53
- - Already implemented in the code!
54
-
55
- **Setup:**
56
-
57
- 1. Create a dynamic feature module in your Android project:
58
- ```
59
- android/
60
- ├── app/
61
- └── flir_sdk/ # New feature module
62
- ├── build.gradle.kts
63
- └── src/main/AndroidManifest.xml
64
- ```
65
-
66
- 2. See detailed setup guide: [PLAY_FEATURE_DELIVERY_GUIDE.md](./PLAY_FEATURE_DELIVERY_GUIDE.md)
67
-
68
- 3. The code automatically uses Play Feature Delivery when available, with GitHub download as fallback.
69
-
70
- **When to use:**
71
- - ✅ Production apps on Google Play Store
72
- - ✅ Want zero hosting costs
73
- - ❌ Development builds (use GitHub download instead)
74
- - ❌ Apps distributed outside Play Store (use GitHub download)
75
-
76
-
77
- ### Prerequisites
78
-
79
- - React Native 0.60+
80
- - Android: minSdk 24, compileSdk 34
81
- - iOS: iOS 13.0+
82
-
83
- ### Quick Install
84
-
85
- #### Android (via local AARs / npm)
86
-
87
- 1. Ensure the AAR files are present under `android/Flir/libs/` (the package includes them by default). If you are updating the SDK, download `android.zip` from the release artifacts and extract the `.aar` files into `android/Flir/libs/`.
88
-
89
- 2. The Gradle configuration in `android/Flir/build.gradle.kts` references those `.aar` files (e.g., `api(files("libs/thermalsdk-release.aar"))`), so you don't need to add external repositories.
90
-
91
- 3. Sync your Gradle files or rebuild your Android app.
92
-
93
- **✅ Android permissions are automatically merged!** The library includes:
94
- - USB host feature (for FLIR ONE USB devices)
95
- - Camera & Internet permissions (for network-based FLIR cameras)
96
-
97
- No manual `AndroidManifest.xml` editing required!
98
-
99
- #### iOS (via CocoaPods)
100
-
101
- 1. Add the following to your `Podfile`:
102
-
103
- ```ruby
104
- # From npm package (recommended)
105
- # The pod will be auto-linked via react-native config
106
-
107
- # OR from GitHub repository
108
- pod 'Flir', :git => 'https://github.com/PraveenOjha/Flir.git', :tag => '2.0.0'
109
-
110
- # OR for local development
111
- pod 'Flir', :podspec => '../path/to/Flir/Flir.podspec'
112
- ```
113
-
114
- 2. Run:
115
-
116
- ```bash
117
- cd ios
118
- pod install
119
- ```
120
-
121
- Note: If you installed `ilabs-flir` via npm, `Podfile` autolinking will declare the `Flir` pod for your app automatically. To avoid duplicates, the published npm package will not contain `Flir.podspec` or in-repo podspecs; they are excluded with `.npmignore`. See `docs/MIGRATION_TO_NPM.md` for migration details if you previously used an in-repo `Flir.podspec`.
122
-
123
- ##### Building Without FLIR SDK (No Paid License)
124
-
125
- If you don't have a paid FLIR developer license, you can build the app without the FLIR SDK. The module will provide fallback stub implementations:
126
-
127
- ```bash
128
- # Set environment variable before pod install
129
- FLIR_DISABLED=1 pod install
130
-
131
- # Or in your Podfile:
132
- ENV['FLIR_DISABLED'] = '1'
133
- ```
134
-
135
- When `FLIR_DISABLED=1`:
136
- - ✅ App compiles without FLIR SDK frameworks
137
- - ✅ All module methods still work (return stubs/fallbacks)
138
- - ✅ Fallback thermal-style gradient images are generated
139
- - ❌ No actual thermal camera functionality
140
-
141
- ##### Including FLIR SDK (Paid License Required)
142
-
143
- To enable full FLIR functionality:
144
-
145
- 1. Download ThermalSDK.xcframework from FLIR
146
- 2. Copy the following xcframeworks to `node_modules/ilabs-flir/ios/Flir/Frameworks/`:
147
- - `ThermalSDK.xcframework`
148
- - `libavcodec.61.dylib.xcframework`
149
- - `libavdevice.61.dylib.xcframework`
150
- - `libavfilter.10.dylib.xcframework`
151
- - `libavformat.61.dylib.xcframework`
152
- - `libavutil.59.dylib.xcframework`
153
- - `liblive666.dylib.xcframework`
154
- - `libswresample.5.dylib.xcframework`
155
- - `libswscale.8.dylib.xcframework`
156
- 3. Run `pod install` (without FLIR_DISABLED)
157
-
158
- Automatic SDK download
159
- ----------------------
160
-
161
- If you install this package from npm/yarn, a `postinstall` hook will try to download the SDK binaries for both iOS and Android automatically using the URLs in `sdk-manifest.json`. The files are placed as follows:
162
-
163
- - iOS: `ios/Flir/Frameworks/` (xcframeworks)
164
- - Android: `android/Flir/libs/` (AAR files)
165
-
166
- Controls:
167
- - Skip automatic download by setting: `FLIR_SDK_SKIP_DOWNLOAD=1`.
168
- - Skip downloading if an expected artifact is already present: `--skip-if-present` when running the `fetch-binaries` script.
169
- - Manual download: `npm run fetch-binaries -- --all` to force a manual download of both targets.
170
-
171
-
172
- 3. **Choose ONE of these options for iOS permissions:**
173
-
174
- **Option A: Automatic (Recommended)** - Using React Native Config Plugin
175
-
176
- Add to your `app.json`:
177
-
178
- ```json
179
- {
180
- "plugins": ["ilabs-flir"]
181
- }
182
- ```
183
-
184
- Then run:
185
- ```bash
186
- npx expo prebuild
187
- ```
188
-
189
- ✅ All Info.plist entries are **automatically added**!
190
-
191
- **Option B: Manual Setup** - Add these entries to your `ios/YourApp/Info.plist`:
192
-
193
- ```xml
194
- <!-- External Accessory Protocols for FLIR ONE devices -->
195
- <key>UISupportedExternalAccessoryProtocols</key>
196
- <array>
197
- <string>com.flir.rosebud.config</string>
198
- <string>com.flir.rosebud.frame</string>
199
- <string>com.flir.rosebud.fileio</string>
200
- </array>
201
-
202
- <!-- Bluetooth permissions for FLIR ONE Edge/Pro -->
203
- <key>NSBluetoothAlwaysUsageDescription</key>
204
- <string>This app requires Bluetooth to connect to FLIR thermal cameras</string>
205
-
206
- <key>NSBluetoothPeripheralUsageDescription</key>
207
- <string>This app uses Bluetooth to communicate with FLIR thermal imaging devices</string>
208
- ```
209
-
210
- ## Usage
211
-
212
- ### Device Discovery
213
-
214
- ```javascript
215
- import { NativeModules, NativeEventEmitter } from 'react-native';
216
-
217
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
218
- const FlirEmitter = new NativeEventEmitter(FlirModule);
219
-
220
- // Listen for device events
221
- FlirEmitter.addListener('FlirDeviceConnected', (event) => {
222
- console.log('FLIR device connected:', event);
223
- });
224
-
225
- FlirEmitter.addListener('FlirDeviceDisconnected', (event) => {
226
- console.log('FLIR device disconnected:', event);
227
- });
228
-
229
- // Start discovering FLIR devices
230
- FlirModule.startDiscovery();
231
-
232
- // Stop discovery
233
- FlirModule.stopDiscovery();
234
- ```
235
-
236
- ### Camera Connection
237
-
238
- ```javascript
239
- // Connect to discovered device
240
- await FlirModule.connect(identityObject);
241
-
242
- // Disconnect
243
- FlirModule.disconnect();
244
-
245
- // Check connection status
246
- const isConnected = await FlirModule.isDeviceConnected();
247
- const deviceInfo = await FlirModule.getConnectedDeviceInfo();
248
- ```
249
-
250
- ### Temperature Measurement
251
-
252
- ```javascript
253
- // Get temperature at specific point (x, y coordinates)
254
- const temperature = await FlirModule.getTemperatureAt(100, 200);
255
- console.log(`Temperature: ${temperature}°C`);
256
-
257
- // Returns null if no thermal image is available
258
- if (temperature !== null) {
259
- console.log(`Detected: ${temperature.toFixed(2)}°C`);
260
- }
261
- ```
262
-
263
- ### Emulator Mode (Development)
264
-
265
- ```javascript
266
- // Check if running in emulator mode
267
- const isEmulator = await FlirModule.isEmulator();
268
-
269
- // Force start emulator mode (for testing without hardware)
270
- await FlirModule.startEmulatorMode();
271
-
272
- // Get device information
273
- const deviceInfo = await FlirModule.getConnectedDeviceInfo();
274
- // Returns: "Emulator (FLIR ONE)" or "Physical device (FLIR ONE)"
275
- ```
276
-
277
- ## API Reference
278
-
279
- ### Methods
280
-
281
- | Method | Parameters | Returns | Description |
282
- |--------|-----------|---------|-------------|
283
- | **Device Discovery** |
284
- | `startDiscovery()` | - | `void` | Start scanning forFLIR devices (USB & Emulator) |
285
- | `stopDiscovery()` | - | `void` | Stop device discovery |
286
- | **Connection** |
287
- | `connect(identity)` | `identity: object` | `Promise<boolean>` | Connect to a discovered FLIR device |
288
- | `disconnect()` | - | `void` | Disconnect from current device |
289
- | `isDeviceConnected()` | - | `Promise<boolean>` | Check if a physical device is connected |
290
- | `getConnectedDeviceInfo()` | - | `Promise<string>` | Get info about connected device |
291
- | **Temperature** |
292
- | `getTemperatureAt(x, y)` | `x: number, y: number` | `Promise<number \| null>` | Get temperature at pixel coordinates |
293
- | **Emulator** |
294
- | `isEmulator()` | - | `Promise<boolean>` | Check if running in emulator mode |
295
- | `startEmulatorMode()` | - | `Promise<boolean>` | Force start emulator mode |
296
-
297
- ### Events
298
-
299
- Listen to these events using `NativeEventEmitter`:
300
-
301
- | Event | Payload | Description |
302
- |-------|---------|-------------|
303
- | `FlirDeviceConnected` | `{ identity, deviceType, isEmulator }` | Fired when a FLIR device connects |
304
- | `FlirDeviceDisconnected` | `{ identity, wasEmulator }` | Fired when a device disconnects |
305
- | `FlirError` | `{ error, type, interface }` | Fired on discovery or connection errors |
306
-
307
- ### Thermal Image Output
308
-
309
- FLIR cameras provide two image types:
310
- - **Thermal Image (MSX)**: Color-mapped thermal data with visual details
311
- - **Photo Image (DC)**: Standard visible light image
312
-
313
- **Color Palettes**: The SDK supports multiple palettes:
314
- - `iron` - Rainbow color map (red=hot, blue=cold) - Default
315
- - `gray` - Grayscale/black-white temperature map
316
- - `arctic`, `rainbow`, etc. - Additional palettes
317
-
318
- > **Note**: Palette switching requires accessing the native ThermalImage API directly. This may be exposed in future versions.
319
-
320
- ## Detailed Usage Guide
321
-
322
- ### Complete Setup Example
323
-
324
- Here's a complete React Native component that demonstrates the full FLIR workflow:
325
-
326
- ```javascript
327
- import React, { useEffect, useState } from 'react';
328
- import {
329
- View,
330
- Text,
331
- Button,
332
- StyleSheet,
333
- NativeModules,
334
- NativeEventEmitter,
335
- Alert,
336
- } from 'react-native';
337
-
338
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
339
- const FlirEmitter = new NativeEventEmitter(FlirModule);
340
-
341
- const FlirThermalCamera = () => {
342
- const [isDiscovering, setIsDiscovering] = useState(false);
343
- const [isConnected, setIsConnected] = useState(false);
344
- const [deviceInfo, setDeviceInfo] = useState('Not connected');
345
- const [temperature, setTemperature] = useState(null);
346
- const [isEmulator, setIsEmulator] = useState(false);
347
-
348
- useEffect(() => {
349
- // Set up event listeners
350
- const deviceConnected = FlirEmitter.addListener(
351
- 'FlirDeviceConnected',
352
- (event) => {
353
- console.log('Device connected:', event);
354
- setIsConnected(true);
355
- setIsDiscovering(false);
356
-
357
- // Get device info after connection
358
- FlirModule.getConnectedDeviceInfo().then(info => {
359
- setDeviceInfo(info);
360
- });
361
- }
362
- );
363
-
364
- const deviceDisconnected = FlirEmitter.addListener(
365
- 'FlirDeviceDisconnected',
366
- (event) => {
367
- console.log('Device disconnected:', event);
368
- setIsConnected(false);
369
- setDeviceInfo('Not connected');
370
- setTemperature(null);
371
- }
372
- );
373
-
374
- const deviceError = FlirEmitter.addListener(
375
- 'FlirError',
376
- (event) => {
377
- console.error('FLIR Error:', event);
378
- Alert.alert('FLIR Error', event.error || 'Unknown error');
379
- }
380
- );
381
-
382
- // Check if we're in emulator mode
383
- FlirModule.isEmulator().then(setIsEmulator);
384
-
385
- // Cleanup listeners on unmount
386
- return () => {
387
- deviceConnected.remove();
388
- deviceDisconnected.remove();
389
- deviceError.remove();
390
-
391
- // Disconnect on unmount
392
- if (isConnected) {
393
- FlirModule.disconnect();
394
- }
395
- };
396
- }, []);
397
-
398
- const handleStartDiscovery = () => {
399
- setIsDiscovering(true);
400
- FlirModule.startDiscovery();
401
- };
402
-
403
- const handleStopDiscovery = () => {
404
- setIsDiscovering(false);
405
- FlirModule.stopDiscovery();
406
- };
407
-
408
- const handleDisconnect = () => {
409
- FlirModule.disconnect();
410
- };
411
-
412
- const handleStartEmulator = async () => {
413
- try {
414
- await FlirModule.startEmulatorMode();
415
- Alert.alert('Success', 'Emulator mode started');
416
- } catch (error) {
417
- Alert.alert('Error', 'Failed to start emulator mode');
418
- }
419
- };
420
-
421
- const handleGetTemperature = async () => {
422
- try {
423
- // Get temperature at center of image (adjust coordinates as needed)
424
- const temp = await FlirModule.getTemperatureAt(160, 120);
425
-
426
- if (temp !== null) {
427
- setTemperature(temp);
428
- } else {
429
- Alert.alert('Info', 'No thermal data available');
430
- }
431
- } catch (error) {
432
- Alert.alert('Error', 'Failed to get temperature');
433
- }
434
- };
435
-
436
- return (
437
- <View style={styles.container}>
438
- <Text style={styles.title}>FLIR Thermal Camera</Text>
439
-
440
- <View style={styles.statusContainer}>
441
- <Text style={styles.statusLabel}>Status:</Text>
442
- <Text style={styles.statusValue}>
443
- {isConnected ? 'Connected' : isDiscovering ? 'Discovering...' : 'Disconnected'}
444
- </Text>
445
- </View>
446
-
447
- <View style={styles.statusContainer}>
448
- <Text style={styles.statusLabel}>Device:</Text>
449
- <Text style={styles.statusValue}>{deviceInfo}</Text>
450
- </View>
451
-
452
- {temperature !== null && (
453
- <View style={styles.statusContainer}>
454
- <Text style={styles.statusLabel}>Temperature:</Text>
455
- <Text style={styles.tempValue}>{temperature.toFixed(2)}°C</Text>
456
- </View>
457
- )}
458
-
459
- <View style={styles.buttonContainer}>
460
- {!isConnected ? (
461
- <>
462
- <Button
463
- title={isDiscovering ? 'Stop Discovery' : 'Start Discovery'}
464
- onPress={isDiscovering ? handleStopDiscovery : handleStartDiscovery}
465
- />
466
- <Button
467
- title="Start Emulator"
468
- onPress={handleStartEmulator}
469
- />
470
- </>
471
- ) : (
472
- <>
473
- <Button
474
- title="Get Temperature"
475
- onPress={handleGetTemperature}
476
- />
477
- <Button
478
- title="Disconnect"
479
- onPress={handleDisconnect}
480
- color="#d9534f"
481
- />
482
- </>
483
- )}
484
- </View>
485
-
486
- {isEmulator && (
487
- <Text style={styles.emulatorNote}>
488
- ℹ️ Running in emulator mode
489
- </Text>
490
- )}
491
- </View>
492
- );
493
- };
494
-
495
- const styles = StyleSheet.create({
496
- container: {
497
- flex: 1,
498
- padding: 20,
499
- backgroundColor: '#fff',
500
- },
501
- title: {
502
- fontSize: 24,
503
- fontWeight: 'bold',
504
- marginBottom: 20,
505
- },
506
- statusContainer: {
507
- flexDirection: 'row',
508
- marginBottom: 10,
509
- },
510
- statusLabel: {
511
- fontWeight: 'bold',
512
- width: 100,
513
- },
514
- statusValue: {
515
- flex: 1,
516
- },
517
- tempValue: {
518
- flex: 1,
519
- fontSize: 18,
520
- fontWeight: 'bold',
521
- color: '#f44336',
522
- },
523
- buttonContainer: {
524
- marginTop: 20,
525
- gap: 10,
526
- },
527
- emulatorNote: {
528
- marginTop: 20,
529
- fontStyle: 'italic',
530
- color: '#666',
531
- },
532
- });
533
-
534
- export default FlirThermalCamera;
535
- ```
536
-
537
- ### Step-by-Step Workflow
538
-
539
- #### 1. Initialize Event Listeners
540
-
541
- Always set up event listeners before starting discovery:
542
-
543
- ```javascript
544
- import { NativeModules, NativeEventEmitter } from 'react-native';
545
-
546
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
547
- const FlirEmitter = new NativeEventEmitter(FlirModule);
548
-
549
- // Listen for device connection
550
- FlirEmitter.addListener('FlirDeviceConnected', (event) => {
551
- console.log('Connected:', event);
552
- // event.identity - Device identity object
553
- // event.deviceType - "device" or "emulator"
554
- // event.isEmulator - boolean
555
- });
556
-
557
- // Listen for disconnection
558
- FlirEmitter.addListener('FlirDeviceDisconnected', (event) => {
559
- console.log('Disconnected:', event);
560
- });
561
-
562
- // Listen for errors
563
- FlirEmitter.addListener('FlirError', (event) => {
564
- console.error('Error:', event.error);
565
- // event.type - "discovery" or "connection"
566
- // event.interface - Communication interface
567
- });
568
- ```
569
-
570
- #### 2. Start Device Discovery
571
-
572
- ```javascript
573
- // Start scanning for FLIR devices
574
- FlirModule.startDiscovery();
575
-
576
- // Discovery will automatically emit events when devices are found
577
- // On Android: Scans for USB devices and emulators
578
- // On iOS: Scans for Lightning, BLE, and emulator devices
579
- ```
580
-
581
- #### 3. Handle Device Connection
582
-
583
- Devices connect automatically when discovered. You'll receive a `FlirDeviceConnected` event:
584
-
585
- ```javascript
586
- FlirEmitter.addListener('FlirDeviceConnected', async (event) => {
587
- // Device is now connected
588
- console.log('Device ID:', event.identity.deviceId);
589
- console.log('Is Emulator:', event.isEmulator);
590
-
591
- // Get additional device information
592
- const info = await FlirModule.getConnectedDeviceInfo();
593
- console.log('Device Info:', info);
594
-
595
- // Check connection status
596
- const connected = await FlirModule.isDeviceConnected();
597
- console.log('Is Connected:', connected);
598
- });
599
- ```
600
-
601
- #### 4. Measure Temperature
602
-
603
- Once connected, you can measure temperature at any point:
604
-
605
- ```javascript
606
- // Get temperature at pixel coordinates (x, y)
607
- const temp = await FlirModule.getTemperatureAt(160, 120);
608
-
609
- if (temp !== null) {
610
- console.log(`Temperature: ${temp.toFixed(2)}°C`);
611
- } else {
612
- console.log('No thermal data available');
613
- }
614
- ```
615
-
616
- **Important Notes**:
617
- - Coordinates are in pixels relative to the thermal image
618
- - Returns `null` if no thermal image is available
619
- - Temperature is in Celsius
620
- - For FLIR ONE: Thermal image is typically 160×120 pixels
621
- - For other cameras: Check device specifications
622
-
623
- #### 5. Disconnect
624
-
625
- ```javascript
626
- // Disconnect from current device
627
- FlirModule.disconnect();
628
-
629
- // This will trigger a FlirDeviceDisconnected event
630
- ```
631
-
632
- #### 6. Stop Discovery
633
-
634
- ```javascript
635
- // Stop scanning for devices
636
- FlirModule.stopDiscovery();
637
- ```
638
-
639
- ### Development Without Hardware (Emulator Mode)
640
-
641
- You can test your app without a physical FLIR device:
642
-
643
- ```javascript
644
- // Check if already in emulator mode
645
- const isEmu = await FlirModule.isEmulator();
646
-
647
- if (!isEmu) {
648
- // Force start emulator mode
649
- await FlirModule.startEmulatorMode();
650
- }
651
-
652
- // Emulator will provide simulated thermal data
653
- // All APIs work the same as with real hardware
654
- ```
655
-
656
- ### Best Practices
657
-
658
- #### 1. Always Clean Up Listeners
659
-
660
- ```javascript
661
- useEffect(() => {
662
- const listeners = [
663
- FlirEmitter.addListener('FlirDeviceConnected', handleConnect),
664
- FlirEmitter.addListener('FlirDeviceDisconnected', handleDisconnect),
665
- FlirEmitter.addListener('FlirError', handleError),
666
- ];
667
-
668
- return () => {
669
- // Remove all listeners on unmount
670
- listeners.forEach(listener => listener.remove());
671
-
672
- // Disconnect device
673
- FlirModule.disconnect();
674
- };
675
- }, []);
676
- ```
677
-
678
- #### 2. Handle Connection State
679
-
680
- ```javascript
681
- const [connectionState, setConnectionState] = useState('disconnected');
682
- // States: 'disconnected', 'discovering', 'connected'
683
-
684
- const handleStartDiscovery = () => {
685
- setConnectionState('discovering');
686
- FlirModule.startDiscovery();
687
- };
688
-
689
- FlirEmitter.addListener('FlirDeviceConnected', () => {
690
- setConnectionState('connected');
691
- });
692
-
693
- FlirEmitter.addListener('FlirDeviceDisconnected', () => {
694
- setConnectionState('disconnected');
695
- });
696
- ```
697
-
698
- #### 3. Error Handling
699
-
700
- ```javascript
701
- try {
702
- const temp = await FlirModule.getTemperatureAt(x, y);
703
-
704
- if (temp === null) {
705
- // No thermal data (device not streaming yet)
706
- console.log('Waiting for thermal data...');
707
- } else {
708
- // Valid temperature
709
- setTemperature(temp);
710
- }
711
- } catch (error) {
712
- console.error('Temperature measurement failed:', error);
713
- }
714
- ```
715
-
716
- #### 4. Temperature Sampling Rate
717
-
718
- Avoid calling `getTemperatureAt` too frequently:
719
-
720
- ```javascript
721
- // ❌ Bad: Calling too frequently
722
- setInterval(() => {
723
- FlirModule.getTemperatureAt(x, y);
724
- }, 16); // 60 FPS - too fast!
725
-
726
- // ✅ Good: Reasonable sampling rate
727
- setInterval(async () => {
728
- const temp = await FlirModule.getTemperatureAt(x, y);
729
- if (temp !== null) {
730
- setTemperature(temp);
731
- }
732
- }, 500); // 2 Hz - good for most applications
733
- ```
734
-
735
- ### Common Use Cases
736
-
737
- #### Use Case 1: Continuous Temperature Monitoring
738
-
739
- ```javascript
740
- const [centerTemp, setCenterTemp] = useState(null);
741
-
742
- useEffect(() => {
743
- if (!isConnected) return;
744
-
745
- // Poll temperature every 500ms
746
- const interval = setInterval(async () => {
747
- const temp = await FlirModule.getTemperatureAt(160, 120);
748
- if (temp !== null) {
749
- setCenterTemp(temp);
750
- }
751
- }, 500);
752
-
753
- return () => clearInterval(interval);
754
- }, [isConnected]);
755
- ```
756
-
757
- #### Use Case 2: Multi-Point Temperature Measurement
758
-
759
- ```javascript
760
- const measureMultiplePoints = async () => {
761
- const points = [
762
- { x: 80, y: 60, name: 'Top Left' },
763
- { x: 240, y: 60, name: 'Top Right' },
764
- { x: 160, y: 120, name: 'Center' },
765
- ];
766
-
767
- const results = await Promise.all(
768
- points.map(async (point) => {
769
- const temp = await FlirModule.getTemperatureAt(point.x, point.y);
770
- return { ...point, temperature: temp };
771
- })
772
- );
773
-
774
- console.log('Temperature readings:', results);
775
- return results;
776
- };
777
- ```
778
-
779
- #### Use Case 3: Auto-Connect on App Start
780
-
781
- ```javascript
782
- useEffect(() => {
783
- // Auto-start discovery when app loads
784
- FlirModule.startDiscovery();
785
-
786
- // Or use emulator if no device available
787
- setTimeout(async () => {
788
- const connected = await FlirModule.isDeviceConnected();
789
- if (!connected) {
790
- console.log('No device found, starting emulator');
791
- await FlirModule.startEmulatorMode();
792
- }
793
- }, 5000); // Wait 5 seconds for device
794
-
795
- return () => {
796
- FlirModule.stopDiscovery();
797
- FlirModule.disconnect();
798
- };
799
- }, []);
800
- ```
801
-
802
- ### Troubleshooting
803
-
804
- #### Problem: "No devices found"
805
-
806
- **Android**:
807
- - Ensure USB debugging is enabled
808
- - Check USB cable is data-capable (not charge-only)
809
- - Grant USB permissions when prompted
810
- - Try unplugging and replugging the FLIR device
811
-
812
- **iOS**:
813
- - Ensure Lightning connector is clean
814
- - Check Info.plist has External Accessory protocols
815
- - For BLE devices: Enable Bluetooth and grant permissions
816
- - Try force-quitting and restarting the app
817
-
818
- #### Problem: "Permission denied"
819
-
820
- **Android**:
821
- ```xml
822
- <!-- Ensure these are in AndroidManifest.xml (auto-added by library) -->
823
- <uses-feature android:name="android.hardware.usb.host" />
824
- <uses-permission android:name="android.permission.CAMERA"/>
825
- ```
826
-
827
- **iOS**:
828
- ```xml
829
- <!-- Ensure these are in Info.plist -->
830
- <key>NSBluetoothAlwaysUsageDescription</key>
831
- <string>Required for FLIR cameras</string>
832
- ```
833
-
834
- #### Problem: "Temperature returns null"
835
-
836
- ```javascript
837
- // Wait for device to start streaming
838
- FlirEmitter.addListener('FlirDeviceConnected', async (event) => {
839
- // Wait a moment for streaming to start
840
- setTimeout(async () => {
841
- const temp = await FlirModule.getTemperatureAt(160, 120);
842
- console.log('Temperature:', temp);
843
- }, 1000);
844
- });
845
- ```
846
-
847
- #### Problem: "App crashes on disconnect"
848
-
849
- ```javascript
850
- // Always check connection before API calls
851
- const getTemperatureSafely = async (x, y) => {
852
- const connected = await FlirModule.isDeviceConnected();
853
-
854
- if (!connected) {
855
- console.log('Device not connected');
856
- return null;
857
- }
858
-
859
- return await FlirModule.getTemperatureAt(x, y);
860
- };
861
- ```
862
-
863
- #### Problem: "Events not firing"
864
-
865
- ```javascript
866
- // Ensure NativeEventEmitter is created with the module
867
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
868
- const FlirEmitter = new NativeEventEmitter(FlirModule); // ✅ Pass module
869
-
870
- // ❌ Wrong:
871
- const FlirEmitter = new NativeEventEmitter(); // No events will fire!
872
- ```
873
-
874
- ### Platform-Specific Notes
875
-
876
- #### Android
877
- - Supports USB FLIR ONE cameras
878
- - Supports network-based FLIR cameras (ACE series)
879
- - Requires physical device (emulator for development only)
880
- - USB permissions handled automatically via `UsbPermissionHandler`
881
-
882
- #### iOS
883
- - Supports Lightning interface (FLIR ONE Classic)
884
- - Supports Bluetooth LE (FLIR ONE Edge/Pro)
885
- - Works on both device and simulator (with emulator mode)
886
- - Requires Info.plist entries (auto-added via config plugin)
887
-
888
- ### Performance Tips
889
-
890
- 1. **Limit temperature polling frequency**: 1-2 Hz is sufficient for most apps
891
- 2. **Disconnect when not in use**: Save battery by disconnecting in background
892
- 3. **Use emulator for UI development**: Build UI without physical hardware
893
- 4. **Cache device info**: Don't call `getConnectedDeviceInfo()` repeatedly
894
-
895
- ## Publishing & CI
896
-
897
- This library is distributed primarily via npm (JavaScript package). Android and iOS native binaries (AAR & XCFramework) are included in the package for compile-time integration.
898
-
899
- Publishing and CI notes:
900
- - If you need to publish the Android artifacts to a Maven repository (for example, for internal use), push the AARs to a Maven repository and update the Gradle configuration accordingly.
901
- - If you run into duplicate-class issues such as `org.slf4j.*`, prefer excluding `org.slf4j:slf4j-api` at the app or module configuration:
902
-
903
- ```kotlin
904
- configurations.all {
905
- exclude(group = "org.slf4j", module = "slf4j-api")
906
- }
907
- ```
908
-
909
- Additional changes may include publishing the AARs into `mavenLocal` during your DR/CI pipeline if you use CI that expects Maven artifacts.
910
-
911
- ## Publishing to CocoaPods
912
-
913
- To publish to CocoaPods Trunk:
914
-
915
- 1. Register your CocoaPods account (first time only):
916
- ```bash
917
- pod trunk register your-email@example.com 'Your Name'
918
- ```
919
-
920
- 2. Validate your podspec (run from repository root):
921
- ```bash
922
- pod spec lint ios/flir/Flir.podspec --allow-warnings
923
- ```
924
-
925
- 3. Push to CocoaPods (run from repository root):
926
- ```bash
927
- pod trunk push ios/flir/Flir.podspec --allow-warnings
928
- ```
929
-
930
- 4. Verify publication:
931
- ```bash
932
- pod search Flir
933
- ```
934
-
935
- **Note:** The `--allow-warnings` flag may be needed for vendored frameworks.
936
-
937
- ## Development
938
-
939
- ### Building Locally
940
-
941
- #### Android
942
-
943
- ```bash
944
- cd android
945
- ./gradlew build
946
- ```
947
-
948
- #### iOS
949
-
950
- ```bash
951
- cd ios/flir
952
- pod install
953
- xcodebuild -workspace Flir.xcworkspace -scheme Flir -configuration Release
954
- ```
955
-
956
- ### Testing
957
-
958
- ```bash
959
- npm test
960
- ```
961
-
962
- ## Emulator Mode
963
-
964
- This wrapper supports emulator mode for development and testing without requiring a physical FLIR device.
965
-
966
- ### Features
967
-
968
- - **Device Detection**: Automatically detect if running on an emulator
969
- - **Fallback Mode**: Use FLIR's built-in emulator when no physical device is available
970
- - **Consistent API**: Same API calls work for both emulator and physical devices
971
-
972
- ### Usage
973
-
974
- ```javascript
975
- import FlirModule from 'react-native-flir';
976
-
977
- // Check if running in emulator mode
978
- const isEmulator = await FlirModule.isEmulator();
979
- console.log('Running in emulator:', isEmulator);
980
-
981
- // Check if a physical device is connected
982
- const isDeviceConnected = await FlirModule.isDeviceConnected();
983
- console.log('Physical device connected:', isDeviceConnected);
984
-
985
- // Get device information
986
- const deviceInfo = await FlirModule.getConnectedDeviceInfo();
987
- console.log('Device info:', deviceInfo);
988
-
989
- // Force start emulator mode (useful for testing)
990
- await FlirModule.startEmulatorMode();
991
- ```
992
-
993
- ### Emulator Features
994
-
995
- - **Simulated Thermal Data**: Provides mock thermal imaging data
996
- - **Temperature Readings**: Returns simulated temperature values
997
- - **Device Events**: Emits connection/disconnection events like physical devices
998
- - **Testing Environment**: Perfect for CI/CD and development without hardware
999
-
1000
- ### Android Emulator Detection
1001
-
1002
- The Android implementation detects emulators by checking:
1003
- - Build properties (`ro.build.fingerprint`, `ro.kernel.qemu`)
1004
- - Hardware characteristics
1005
- - Build model and manufacturer
1006
-
1007
- ### iOS Simulator Detection
1008
-
1009
- The iOS implementation uses:
1010
- - FLIR SDK's built-in emulator interface
1011
- - Runtime environment detection
1012
- - Simulator-specific device identifiers
1013
-
1014
- ## Requirements
1015
-
1016
- ### Android
1017
- - Android SDK 24+
1018
- - Kotlin 1.9.0+
1019
- - Gradle 8.1.0+
1020
- - Java 21
1021
-
1022
- ### iOS
1023
- - iOS 13.0+
1024
- - Xcode 14+
1025
- - CocoaPods 1.10+
1026
-
1027
- ## FLIR SDK Licensing
1028
-
1029
- This React Native wrapper is provided under the MIT license, but the FLIR thermal imaging SDKs have their own licensing requirements:
1030
-
1031
- 1. **Commercial Use**: Requires a commercial license from FLIR
1032
- 2. **Development License**: Required even for development and testing
1033
- 3. **Distribution**: You cannot distribute FLIR SDK libraries without proper licensing
1034
-
1035
- **Important**: The FLIR SDK libraries included in this repository are placeholders. You must:
1036
- - Register at [FLIR Developer Portal](https://www.flir.com/developer/mobile-sdk/)
1037
- - Download your licensed SDK versions
1038
- - Replace the placeholder files with your licensed libraries
1039
-
1040
- ## License
1041
-
1042
- **Wrapper Code**: MIT License (this React Native wrapper)
1043
- **FLIR SDK**: Proprietary license from FLIR Systems (see FLIR developer portal)
1044
-
1045
- By using this wrapper, you agree to comply with FLIR's licensing terms and conditions. License - see LICENSE file for details
1046
-
1047
- ### Disclaimer
1048
-
1049
- Flir SDKs may not be used to create apps intended for medical or health purposes; please refer to the SDK license agreement for more detailed information.
1050
-
1051
- ## Contributing
1052
-
1053
- Contributions are welcome! Please feel free to submit a Pull Request.
1054
-
1055
- ## Support
1056
-
1057
- For issues and questions:
1058
- - 🐛 [Report a bug](https://github.com/PraveenOjha/Flir/issues)
1059
- - 💡 [Request a feature](https://github.com/PraveenOjha/Flir/issues)
1060
- - 📖 [Documentation](https://github.com/PraveenOjha/Flir/wiki)
1061
-
1062
- ## Credits
1063
-
1064
- Built with the FLIR Thermal SDK
1065
-
1066
- ---
1067
-
1068
- Made with ❤️ by [Praveen Ojha](https://github.com/PraveenOjha)
1
+ # ilabs-flir (Modular Thermal Camera Plugin)
2
+
3
+ A professional, high-fidelity React Native wrapper for the FLIR Thermal SDK (iOS & Android). This package is designed as a standalone, "Lego-like" module that can be dropped into any React Native project to add professional-grade thermal imaging capabilities.
4
+
5
+ ## 🚀 Installation
6
+
7
+ ```bash
8
+ npm install ilabs-flir
9
+ ```
10
+
11
+ ### Automatic Native Configuration
12
+ This package includes a sync script that automatically patches your iOS `Info.plist` and Android `AndroidManifest.xml` with required FLIR permissions and hardware protocols.
13
+
14
+ ```bash
15
+ node node_modules/ilabs-flir/scripts/sync-native.js
16
+ ```
17
+
18
+ ---
19
+
20
+ ## 📺 Usage
21
+
22
+ ### 1. The Thermal Preview Component
23
+ The most efficient way to display a live thermal stream. This component renders directly from the native buffer, bypassing the React Native bridge for maximum performance.
24
+
25
+ ```javascript
26
+ import { ThermalPreview } from 'ilabs-flir';
27
+
28
+ const MyCameraApp = () => {
29
+ return (
30
+ <ThermalPreview
31
+ style={{ flex: 1 }}
32
+ onFrameUpdate={(event) => {
33
+ // Optional: track frame metadata
34
+ console.log("Frame size:", event.nativeEvent.width, event.nativeEvent.height);
35
+ }}
36
+ />
37
+ );
38
+ }
39
+ ```
40
+
41
+ ### 2. Controlling the Camera
42
+ The `FlirModule` provides a robust API for managing the thermal lifecycle.
43
+
44
+ ```javascript
45
+ import { FlirModule } from 'ilabs-flir';
46
+
47
+ // 1. Start discovery (finds USB-C, Lightning, and Edge/Wireless devices)
48
+ await FlirModule.startDiscovery();
49
+
50
+ // 2. Connect once a device is found
51
+ await FlirModule.connectToDevice(deviceId);
52
+
53
+ // 3. Change Palette
54
+ // Supported: "WhiteHot", "Iron", "Rainbow", "Arctic", "Lava", "Coldest", "Hottest", "Wheel"
55
+ await FlirModule.setPalette("Iron");
56
+
57
+ // 4. Get Spot Temperature
58
+ const temp = await FlirModule.getTemperatureAt(320, 240);
59
+ console.log(`Surface Temp: ${temp.toFixed(1)}°C`);
60
+ ```
61
+
62
+ ### 3. Accessing the Stream (Bitmap)
63
+ If you need to process thermal frames manually (e.g., for AI/ML or custom overlays), you can access the latest frame as a bitmap.
64
+
65
+ ```javascript
66
+ // Get the latest frame as a temporary file path
67
+ const path = await FlirModule.getLatestFramePath();
68
+
69
+ // Get the latest frame as a Base64 string
70
+ const { base64 } = await FlirModule.getLatestFrameBitmap();
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 🏗 Modular Architecture (Zero-Entanglement)
76
+
77
+ `ilabs-flir` is built to be "Toggleable". In your main app, you can use **Reflection-based bridges** (Java/Objective-C) to interact with this module. This allows your main application to compile and run perfectly even if the `ilabs-flir` module is physically removed from the project.
78
+
79
+ ### Why this matters:
80
+ - **Build Speed**: Disable thermal features in dev builds to speed up compilation.
81
+ - **App Store Compliance**: Toggle thermal permissions on/off based on build targets.
82
+ - **Modular Maintenance**: Update the FLIR SDK within this package without touching your core application logic.
83
+
84
+ ---
85
+
86
+ ## 📋 Requirements
87
+ - **iOS**: 13.0+, Physical device required (FLIR ONE/Edge).
88
+ - **Android**: SDK 24+, Kotlin 1.9+, Java 21+.
89
+ - **Hardware**: FLIR ONE Gen 3, FLIR ONE Pro, or FLIR ONE Edge Pro.
90
+
91
+ ---
92
+
93
+ ## 📄 License
94
+ **Plugin Wrapper**: MIT.
95
+ **FLIR SDK**: Proprietary. You must comply with [FLIR's Developer Terms](https://www.flir.com/developer/mobile-sdk/).
96
+
97
+ ---
98
+
99
+ Made with ❤️ by [Praveen Ojha](https://github.com/PraveenOjha)