ilabs-flir 2.0.4 → 2.0.5

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