ilabs-flir 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/Flir.podspec +31 -31
  2. package/README.md +1271 -1271
  3. package/android/Flir/build.gradle.kts +85 -80
  4. package/android/Flir/src/main/AndroidManifest.xml +31 -31
  5. package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCodeException.java +14 -0
  6. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ImageBuffer.java +11 -0
  7. package/android/Flir/src/main/java/com/flir/thermalsdk/image/JavaImageBuffer.java +35 -0
  8. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Palette.java +15 -0
  9. package/android/Flir/src/main/java/com/flir/thermalsdk/image/PaletteManager.java +16 -0
  10. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Point.java +11 -0
  11. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalImage.java +23 -0
  12. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalValue.java +9 -0
  13. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CameraType.java +8 -0
  14. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CommunicationInterface.java +16 -0
  15. package/android/Flir/src/main/java/com/flir/thermalsdk/live/Identity.java +23 -0
  16. package/android/Flir/src/main/java/com/flir/thermalsdk/live/IpSettings.java +9 -0
  17. package/android/Flir/src/main/java/com/flir/thermalsdk/live/connectivity/ConnectionStatusListener.java +7 -0
  18. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnReceived.java +5 -0
  19. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnRemoteError.java +7 -0
  20. package/android/Flir/src/main/java/flir/android/CameraHandler.java +224 -194
  21. package/android/Flir/src/main/java/flir/android/FlirCommands.java +111 -0
  22. package/android/Flir/src/main/java/flir/android/FlirConnectionManager.java +354 -0
  23. package/android/Flir/src/main/java/flir/android/FlirController.kt +11 -11
  24. package/android/Flir/src/main/java/flir/android/FlirDiscoveryManager.java +236 -0
  25. package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +75 -75
  26. package/android/Flir/src/main/java/flir/android/FlirDownloadPackage.kt +16 -16
  27. package/android/Flir/src/main/java/flir/android/FlirFrameCache.kt +6 -6
  28. package/android/Flir/src/main/java/flir/android/FlirManager.kt +254 -248
  29. package/android/Flir/src/main/java/flir/android/FlirModule.kt +74 -74
  30. package/android/Flir/src/main/java/flir/android/FlirPackage.kt +19 -19
  31. package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +195 -195
  32. package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +890 -0
  33. package/android/Flir/src/main/java/flir/android/FlirStatus.kt +12 -12
  34. package/android/Flir/src/main/java/flir/android/FlirView.kt +48 -48
  35. package/android/Flir/src/main/java/flir/android/FlirViewManager.kt +13 -13
  36. package/android/Flir/src/main/java/flir/android/FrameDataHolder.java +14 -14
  37. package/app.plugin.js +264 -264
  38. package/expo-module.config.json +5 -5
  39. package/ios/Flir/Framework/ThermalSDK/FLIRBattery.h +76 -76
  40. package/ios/Flir/Framework/ThermalSDK/FLIRCalibration.h +108 -108
  41. package/ios/Flir/Framework/ThermalSDK/FLIRCamera.h +156 -156
  42. package/ios/Flir/Framework/ThermalSDK/FLIRCameraDeviceInfo.h +53 -53
  43. package/ios/Flir/Framework/ThermalSDK/FLIRCameraEvent.h +132 -132
  44. package/ios/Flir/Framework/ThermalSDK/FLIRCameraImport.h +204 -204
  45. package/ios/Flir/Framework/ThermalSDK/FLIRColorDistributionSettings.h +204 -204
  46. package/ios/Flir/Framework/ThermalSDK/FLIRColorizer.h +82 -82
  47. package/ios/Flir/Framework/ThermalSDK/FLIRDiscoveredCamera.h +44 -44
  48. package/ios/Flir/Framework/ThermalSDK/FLIRDiscovery.h +132 -132
  49. package/ios/Flir/Framework/ThermalSDK/FLIRDisplaySettings.h +29 -29
  50. package/ios/Flir/Framework/ThermalSDK/FLIRFocus.h +70 -70
  51. package/ios/Flir/Framework/ThermalSDK/FLIRFusion.h +192 -192
  52. package/ios/Flir/Framework/ThermalSDK/FLIRFusionController.h +136 -136
  53. package/ios/Flir/Framework/ThermalSDK/FLIRFusionTransformation.h +35 -35
  54. package/ios/Flir/Framework/ThermalSDK/FLIRIdentity.h +264 -264
  55. package/ios/Flir/Framework/ThermalSDK/FLIRImageBase.h +196 -196
  56. package/ios/Flir/Framework/ThermalSDK/FLIRImageColorizer.h +26 -26
  57. package/ios/Flir/Framework/ThermalSDK/FLIRImageStatistics.h +61 -61
  58. package/ios/Flir/Framework/ThermalSDK/FLIRIsotherms.h +208 -208
  59. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementArea.h +38 -38
  60. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementCollection.h +147 -147
  61. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDelta.h +62 -62
  62. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDimensions.h +33 -33
  63. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementEllipse.h +49 -49
  64. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementLine.h +66 -66
  65. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementMarker.h +69 -69
  66. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementParameters.h +41 -41
  67. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementRectangle.h +36 -36
  68. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementReference.h +27 -27
  69. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementShape.h +46 -46
  70. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementSpot.h +33 -33
  71. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementsController.h +160 -160
  72. package/ios/Flir/Framework/ThermalSDK/FLIRMeterLinkSensorPoll.h +247 -247
  73. package/ios/Flir/Framework/ThermalSDK/FLIROverlayController.h +27 -27
  74. package/ios/Flir/Framework/ThermalSDK/FLIRPalette.h +60 -60
  75. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteController.h +36 -36
  76. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteManager.h +97 -97
  77. package/ios/Flir/Framework/ThermalSDK/FLIRQuantification.h +55 -55
  78. package/ios/Flir/Framework/ThermalSDK/FLIRRemoteControl.h +393 -393
  79. package/ios/Flir/Framework/ThermalSDK/FLIRRenderer.h +35 -35
  80. package/ios/Flir/Framework/ThermalSDK/FLIRRendererImpl.h +17 -17
  81. package/ios/Flir/Framework/ThermalSDK/FLIRScale.h +99 -99
  82. package/ios/Flir/Framework/ThermalSDK/FLIRScaleController.h +44 -44
  83. package/ios/Flir/Framework/ThermalSDK/FLIRStream.h +109 -109
  84. package/ios/Flir/Framework/ThermalSDK/FLIRStreamer.h +124 -124
  85. package/ios/Flir/Framework/ThermalSDK/FLIRSystem.h +40 -40
  86. package/ios/Flir/Framework/ThermalSDK/FLIRTemperatureRange.h +43 -43
  87. package/ios/Flir/Framework/ThermalSDK/FLIRThermalDelta.h +77 -77
  88. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImage.h +331 -331
  89. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImageFile.h +56 -56
  90. package/ios/Flir/Framework/ThermalSDK/FLIRThermalParameters.h +31 -31
  91. package/ios/Flir/Framework/ThermalSDK/FLIRThermalValue.h +92 -92
  92. package/ios/Flir/Framework/ThermalSDK/FLIRWirelessCameraDetails.h +88 -88
  93. package/ios/Flir/Framework/ThermalSDK/ThermalSDK.h +73 -73
  94. package/ios/Flir/SDKLoader/FlirSDKLoader.m +13 -13
  95. package/ios/Flir/SDKLoader/FlirSDKLoader.swift +175 -175
  96. package/ios/Flir/src/FlirEventEmitter.h +12 -12
  97. package/ios/Flir/src/FlirEventEmitter.m +33 -33
  98. package/ios/Flir/src/FlirModule.h +10 -10
  99. package/ios/Flir/src/FlirModule.m +381 -381
  100. package/ios/Flir/src/FlirPreviewView.h +13 -13
  101. package/ios/Flir/src/FlirPreviewView.m +24 -24
  102. package/ios/Flir/src/FlirState.h +20 -20
  103. package/ios/Flir/src/FlirState.m +79 -79
  104. package/ios/Flir/src/FlirViewManager.h +9 -9
  105. package/ios/Flir/src/FlirViewManager.m +16 -16
  106. package/package.json +60 -60
  107. package/react-native.config.js +14 -14
  108. package/scripts/copy_ios_libs.sh +32 -32
  109. package/scripts/create_stubs.py +174 -174
  110. package/scripts/download-sdk.js +62 -62
  111. package/scripts/prepare-binaries.sh +171 -171
  112. package/sdk-manifest.json +30 -30
  113. package/src/FlirDownload.ts +78 -78
  114. package/src/index.d.ts +17 -17
  115. package/src/index.js +7 -7
  116. package/src/index.ts +7 -7
package/README.md CHANGED
@@ -1,1271 +1,1271 @@
1
- # FLIR Thermal SDK - React Native
2
-
3
- A React Native wrapper for the FLIR Thermal SDK, providing thermal imaging capabilities for both Android and iOS applications.
4
-
5
- [![](https://jitpack.io/v/PraveenOjha/Flir.svg)](https://jitpack.io/#PraveenOjha/Flir)
6
-
7
- ## Features
8
-
9
- - 📱 Cross-platform support (Android & iOS)
10
- - 🔥 Real-time thermal imaging
11
- - 📸 Thermal image capture and processing
12
- - 🎨 Customizable color palettes
13
- - 📊 Temperature measurement and analysis
14
- - ⚡ **Automatic permission setup** (no manual manifest/plist editing required)
15
- - 📦 **On-demand SDK download** (~100MB downloaded only when needed)
16
- - 🔌 USB & Bluetooth device support
17
- - 🎮 Emulator mode for development without hardware
18
-
19
- ## Installation
20
-
21
- ### npm Installation
22
-
23
- ```bash
24
- npm install ilabs-flir
25
- # or
26
- yarn add ilabs-flir
27
- ```
28
-
29
- ### On-Demand SDK Download
30
-
31
- The FLIR SDK binaries (~100MB) are **not bundled** with this package. They are downloaded on-demand when thermal features are first used.
32
-
33
- #### Quick Start
34
-
35
- ```typescript
36
- import React, { useState, useEffect } from 'react';
37
- import { View, Text, Button, ActivityIndicator } from 'react-native';
38
- import { FlirDownload, FlirModule } from 'ilabs-flir';
39
-
40
- function ThermalCamera() {
41
- const [sdkReady, setSdkReady] = useState(false);
42
- const [downloading, setDownloading] = useState(false);
43
- const [progress, setProgress] = useState(0);
44
-
45
- useEffect(() => {
46
- checkAndDownloadSDK();
47
- }, []);
48
-
49
- const checkAndDownloadSDK = async () => {
50
- try {
51
- // Check if SDK is already available
52
- const available = await FlirDownload.isAvailable();
53
-
54
- if (available) {
55
- setSdkReady(true);
56
- return;
57
- }
58
-
59
- // Get download size to show user
60
- const size = await FlirDownload.getDownloadSizeFormatted(); // "100 MB"
61
- console.log(`SDK needs to be downloaded: ${size}`);
62
-
63
- // Download with progress tracking
64
- setDownloading(true);
65
- await FlirDownload.download((progress) => {
66
- setProgress(progress.percent);
67
- console.log(`Downloading: ${progress.percent.toFixed(0)}%`);
68
- });
69
-
70
- setDownloading(false);
71
- setSdkReady(true);
72
- console.log('SDK ready!');
73
- } catch (error) {
74
- console.error('SDK download failed:', error);
75
- setDownloading(false);
76
- }
77
- };
78
-
79
- if (downloading) {
80
- return (
81
- <View>
82
- <Text>Downloading FLIR SDK...</Text>
83
- <Text>{progress.toFixed(0)}%</Text>
84
- <ActivityIndicator size="large" />
85
- </View>
86
- );
87
- }
88
-
89
- if (!sdkReady) {
90
- return (
91
- <View>
92
- <Text>FLIR SDK not available</Text>
93
- <Button title="Download SDK" onPress={checkAndDownloadSDK} />
94
- </View>
95
- );
96
- }
97
-
98
- // SDK is ready, use FLIR features
99
- return (
100
- <View>
101
- <Button title="Start Discovery" onPress={() => FlirModule.startDiscovery()} />
102
- </View>
103
- );
104
- }
105
- ```
106
-
107
- #### FlirDownload API Reference
108
-
109
- | Method | Returns | Description |
110
- |--------|---------|-------------|
111
- | `isAvailable()` | `Promise<boolean>` | Check if SDK is already downloaded |
112
- | `getDownloadSize()` | `Promise<number>` | Get download size in bytes |
113
- | `getDownloadSizeFormatted()` | `Promise<string>` | Get human-readable size (e.g., "100 MB") |
114
- | `download(onProgress?)` | `Promise<void>` | Download SDK with optional progress callback |
115
- | `cancel()` | `void` | Cancel ongoing download |
116
- | `delete()` | `Promise<boolean>` | Remove downloaded SDK |
117
-
118
- #### Progress Callback
119
-
120
- ```typescript
121
- await FlirDownload.download((progress) => {
122
- console.log('Downloaded:', progress.bytesDownloaded);
123
- console.log('Total:', progress.totalBytes);
124
- console.log('Percent:', progress.percent); // 0-100
125
- });
126
- ```
127
-
128
- #### Best Practices
129
-
130
- **1. Download on First Launch**
131
- ```typescript
132
- // In your App.tsx or main component
133
- useEffect(() => {
134
- const initSDK = async () => {
135
- const available = await FlirDownload.isAvailable();
136
- if (!available) {
137
- // Show a modal or screen explaining the download
138
- await FlirDownload.download((progress) => {
139
- updateProgressBar(progress.percent);
140
- });
141
- }
142
- };
143
- initSDK();
144
- }, []);
145
- ```
146
-
147
- **2. Download Before Feature Access**
148
- ```typescript
149
- const openThermalCamera = async () => {
150
- const available = await FlirDownload.isAvailable();
151
-
152
- if (!available) {
153
- Alert.alert(
154
- 'Download Required',
155
- 'FLIR SDK needs to be downloaded (100 MB). Continue?',
156
- [
157
- { text: 'Cancel', style: 'cancel' },
158
- {
159
- text: 'Download',
160
- onPress: async () => {
161
- await FlirDownload.download((p) => console.log(p.percent));
162
- navigation.navigate('ThermalCamera');
163
- }
164
- }
165
- ]
166
- );
167
- } else {
168
- navigation.navigate('ThermalCamera');
169
- }
170
- };
171
- ```
172
-
173
- **3. Handle Errors**
174
- ```typescript
175
- try {
176
- await FlirDownload.download((progress) => {
177
- setProgress(progress.percent);
178
- });
179
- } catch (error) {
180
- if (error.message.includes('Checksum')) {
181
- Alert.alert('Download Error', 'File verification failed. Please try again.');
182
- } else if (error.message.includes('Network')) {
183
- Alert.alert('Network Error', 'Please check your internet connection.');
184
- } else {
185
- Alert.alert('Error', 'SDK download failed. Please try again.');
186
- }
187
- }
188
- ```
189
-
190
- **4. Show Download Size First**
191
- ```typescript
192
- const size = await FlirDownload.getDownloadSizeFormatted();
193
- Alert.alert(
194
- 'Download Required',
195
- `FLIR SDK (${size}) needs to be downloaded. This is a one-time download.`,
196
- [
197
- { text: 'Cancel' },
198
- { text: 'Download', onPress: () => downloadSDK() }
199
- ]
200
- );
201
- ```
202
-
203
- #### Where SDKs Are Downloaded From
204
-
205
- - **Android**: GitHub Releases (with optional Google Play Feature Delivery support)
206
- - **iOS**: GitHub Releases
207
- - **Source**: https://github.com/PraveenOjha/flir-sdk-binaries/releases
208
- - **Security**: SHA256 checksum verification on all downloads
209
-
210
- #### Offline Usage
211
-
212
- Once downloaded, the SDK is stored permanently on the device:
213
- - **iOS**: `Application Support/FlirSDK/`
214
- - **Android**: App's internal storage
215
-
216
- No internet connection needed after initial download.
217
-
218
- ### Manual SDK Download (Development)
219
-
220
- For development, you can pre-download the SDK:
221
-
222
- ```bash
223
- npm run download-sdk ios
224
- npm run download-sdk android
225
- ```
226
-
227
- ### Advanced: Google Play Feature Delivery (Android)
228
-
229
- 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.
230
-
231
- **Benefits:**
232
- - ✅ Zero hosting and bandwidth costs (Google serves the files)
233
- - ✅ Faster downloads via Google's CDN
234
- - ✅ Automatic updates with your app
235
- - ✅ Already implemented in the code!
236
-
237
- **Setup:**
238
-
239
- 1. Create a dynamic feature module in your Android project:
240
- ```
241
- android/
242
- ├── app/
243
- └── flir_sdk/ # New feature module
244
- ├── build.gradle.kts
245
- └── src/main/AndroidManifest.xml
246
- ```
247
-
248
- 2. See detailed setup guide: [PLAY_FEATURE_DELIVERY_GUIDE.md](./PLAY_FEATURE_DELIVERY_GUIDE.md)
249
-
250
- 3. The code automatically uses Play Feature Delivery when available, with GitHub download as fallback.
251
-
252
- **When to use:**
253
- - ✅ Production apps on Google Play Store
254
- - ✅ Want zero hosting costs
255
- - ❌ Development builds (use GitHub download instead)
256
- - ❌ Apps distributed outside Play Store (use GitHub download)
257
-
258
-
259
- ### Prerequisites
260
-
261
- - React Native 0.60+
262
- - Android: minSdk 24, compileSdk 34
263
- - iOS: iOS 13.0+
264
-
265
- ### Quick Install
266
-
267
- #### Android (via JitPack)
268
-
269
- 1. Add JitPack repository to your root `build.gradle`:
270
-
271
- ```gradle
272
- allprojects {
273
- repositories {
274
- // ... other repositories
275
- maven { url 'https://jitpack.io' }
276
- }
277
- }
278
- ```
279
-
280
- 2. Add the dependency to your app's `build.gradle`:
281
-
282
- ```gradle
283
- dependencies {
284
- implementation 'com.github.PraveenOjha:Flir:1.0.0'
285
- }
286
- ```
287
-
288
- 3. Sync your Gradle files.
289
-
290
- **✅ Android permissions are automatically merged!** The library includes:
291
- - USB host feature (for FLIR ONE USB devices)
292
- - Camera & Internet permissions (for network-based FLIR cameras)
293
-
294
- No manual `AndroidManifest.xml` editing required!
295
-
296
- #### iOS (via CocoaPods)
297
-
298
- 1. Add the following to your `Podfile`:
299
-
300
- ```ruby
301
- # From GitHub repository (recommended)
302
- pod 'Flir', :git => 'https://github.com/PraveenOjha/Flir.git', :tag => '1.0.0'
303
-
304
- # OR for local development
305
- pod 'Flir', :podspec => '../path/to/Flir/Flir.podspec'
306
- ```
307
-
308
- 2. Run:
309
-
310
- ```bash
311
- cd ios
312
- pod install
313
- ```
314
-
315
- 3. **Choose ONE of these options for iOS permissions:**
316
-
317
- **Option A: Automatic (Recommended)** - Using React Native Config Plugin
318
-
319
- Add to your `app.json`:
320
-
321
- ```json
322
- {
323
- "plugins": ["ilabs-flir"]
324
- }
325
- ```
326
-
327
- Then run:
328
- ```bash
329
- npx expo prebuild
330
- ```
331
-
332
- ✅ All Info.plist entries are **automatically added**!
333
-
334
- **Option B: Manual Setup** - Add these entries to your `ios/YourApp/Info.plist`:
335
-
336
- ```xml
337
- <!-- External Accessory Protocols for FLIR ONE devices -->
338
- <key>UISupportedExternalAccessoryProtocols</key>
339
- <array>
340
- <string>com.flir.rosebud.config</string>
341
- <string>com.flir.rosebud.frame</string>
342
- <string>com.flir.rosebud.fileio</string>
343
- </array>
344
-
345
- <!-- Bluetooth permissions for FLIR ONE Edge/Pro -->
346
- <key>NSBluetoothAlwaysUsageDescription</key>
347
- <string>This app requires Bluetooth to connect to FLIR thermal cameras</string>
348
-
349
- <key>NSBluetoothPeripheralUsageDescription</key>
350
- <string>This app uses Bluetooth to communicate with FLIR thermal imaging devices</string>
351
- ```
352
-
353
- ## Usage
354
-
355
- ### Device Discovery
356
-
357
- ```javascript
358
- import { NativeModules, NativeEventEmitter } from 'react-native';
359
-
360
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
361
- const FlirEmitter = new NativeEventEmitter(FlirModule);
362
-
363
- // Listen for device events
364
- FlirEmitter.addListener('FlirDeviceConnected', (event) => {
365
- console.log('FLIR device connected:', event);
366
- });
367
-
368
- FlirEmitter.addListener('FlirDeviceDisconnected', (event) => {
369
- console.log('FLIR device disconnected:', event);
370
- });
371
-
372
- // Start discovering FLIR devices
373
- FlirModule.startDiscovery();
374
-
375
- // Stop discovery
376
- FlirModule.stopDiscovery();
377
- ```
378
-
379
- ### Camera Connection
380
-
381
- ```javascript
382
- // Connect to discovered device
383
- await FlirModule.connect(identityObject);
384
-
385
- // Disconnect
386
- FlirModule.disconnect();
387
-
388
- // Check connection status
389
- const isConnected = await FlirModule.isDeviceConnected();
390
- const deviceInfo = await FlirModule.getConnectedDeviceInfo();
391
- ```
392
-
393
- ### Temperature Measurement
394
-
395
- ```javascript
396
- // Get temperature at specific point (x, y coordinates)
397
- const temperature = await FlirModule.getTemperatureAt(100, 200);
398
- console.log(`Temperature: ${temperature}°C`);
399
-
400
- // Returns null if no thermal image is available
401
- if (temperature !== null) {
402
- console.log(`Detected: ${temperature.toFixed(2)}°C`);
403
- }
404
- ```
405
-
406
- ### Emulator Mode (Development)
407
-
408
- ```javascript
409
- // Check if running in emulator mode
410
- const isEmulator = await FlirModule.isEmulator();
411
-
412
- // Force start emulator mode (for testing without hardware)
413
- await FlirModule.startEmulatorMode();
414
-
415
- // Get device information
416
- const deviceInfo = await FlirModule.getConnectedDeviceInfo();
417
- // Returns: "Emulator (FLIR ONE)" or "Physical device (FLIR ONE)"
418
- ```
419
-
420
- ## API Reference
421
-
422
- ### Methods
423
-
424
- | Method | Parameters | Returns | Description |
425
- |--------|-----------|---------|-------------|
426
- | **Device Discovery** |
427
- | `startDiscovery()` | - | `void` | Start scanning forFLIR devices (USB & Emulator) |
428
- | `stopDiscovery()` | - | `void` | Stop device discovery |
429
- | **Connection** |
430
- | `connect(identity)` | `identity: object` | `Promise<boolean>` | Connect to a discovered FLIR device |
431
- | `disconnect()` | - | `void` | Disconnect from current device |
432
- | `isDeviceConnected()` | - | `Promise<boolean>` | Check if a physical device is connected |
433
- | `getConnectedDeviceInfo()` | - | `Promise<string>` | Get info about connected device |
434
- | **Temperature** |
435
- | `getTemperatureAt(x, y)` | `x: number, y: number` | `Promise<number \| null>` | Get temperature at pixel coordinates |
436
- | **Emulator** |
437
- | `isEmulator()` | - | `Promise<boolean>` | Check if running in emulator mode |
438
- | `startEmulatorMode()` | - | `Promise<boolean>` | Force start emulator mode |
439
-
440
- ### Events
441
-
442
- Listen to these events using `NativeEventEmitter`:
443
-
444
- | Event | Payload | Description |
445
- |-------|---------|-------------|
446
- | `FlirDeviceConnected` | `{ identity, deviceType, isEmulator }` | Fired when a FLIR device connects |
447
- | `FlirDeviceDisconnected` | `{ identity, wasEmulator }` | Fired when a device disconnects |
448
- | `FlirError` | `{ error, type, interface }` | Fired on discovery or connection errors |
449
-
450
- ### Thermal Image Output
451
-
452
- FLIR cameras provide two image types:
453
- - **Thermal Image (MSX)**: Color-mapped thermal data with visual details
454
- - **Photo Image (DC)**: Standard visible light image
455
-
456
- **Color Palettes**: The SDK supports multiple palettes:
457
- - `iron` - Rainbow color map (red=hot, blue=cold) - Default
458
- - `gray` - Grayscale/black-white temperature map
459
- - `arctic`, `rainbow`, etc. - Additional palettes
460
-
461
- > **Note**: Palette switching requires accessing the native ThermalImage API directly. This may be exposed in future versions.
462
-
463
- ## Detailed Usage Guide
464
-
465
- ### Complete Setup Example
466
-
467
- Here's a complete React Native component that demonstrates the full FLIR workflow:
468
-
469
- ```javascript
470
- import React, { useEffect, useState } from 'react';
471
- import {
472
- View,
473
- Text,
474
- Button,
475
- StyleSheet,
476
- NativeModules,
477
- NativeEventEmitter,
478
- Alert,
479
- } from 'react-native';
480
-
481
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
482
- const FlirEmitter = new NativeEventEmitter(FlirModule);
483
-
484
- const FlirThermalCamera = () => {
485
- const [isDiscovering, setIsDiscovering] = useState(false);
486
- const [isConnected, setIsConnected] = useState(false);
487
- const [deviceInfo, setDeviceInfo] = useState('Not connected');
488
- const [temperature, setTemperature] = useState(null);
489
- const [isEmulator, setIsEmulator] = useState(false);
490
-
491
- useEffect(() => {
492
- // Set up event listeners
493
- const deviceConnected = FlirEmitter.addListener(
494
- 'FlirDeviceConnected',
495
- (event) => {
496
- console.log('Device connected:', event);
497
- setIsConnected(true);
498
- setIsDiscovering(false);
499
-
500
- // Get device info after connection
501
- FlirModule.getConnectedDeviceInfo().then(info => {
502
- setDeviceInfo(info);
503
- });
504
- }
505
- );
506
-
507
- const deviceDisconnected = FlirEmitter.addListener(
508
- 'FlirDeviceDisconnected',
509
- (event) => {
510
- console.log('Device disconnected:', event);
511
- setIsConnected(false);
512
- setDeviceInfo('Not connected');
513
- setTemperature(null);
514
- }
515
- );
516
-
517
- const deviceError = FlirEmitter.addListener(
518
- 'FlirError',
519
- (event) => {
520
- console.error('FLIR Error:', event);
521
- Alert.alert('FLIR Error', event.error || 'Unknown error');
522
- }
523
- );
524
-
525
- // Check if we're in emulator mode
526
- FlirModule.isEmulator().then(setIsEmulator);
527
-
528
- // Cleanup listeners on unmount
529
- return () => {
530
- deviceConnected.remove();
531
- deviceDisconnected.remove();
532
- deviceError.remove();
533
-
534
- // Disconnect on unmount
535
- if (isConnected) {
536
- FlirModule.disconnect();
537
- }
538
- };
539
- }, []);
540
-
541
- const handleStartDiscovery = () => {
542
- setIsDiscovering(true);
543
- FlirModule.startDiscovery();
544
- };
545
-
546
- const handleStopDiscovery = () => {
547
- setIsDiscovering(false);
548
- FlirModule.stopDiscovery();
549
- };
550
-
551
- const handleDisconnect = () => {
552
- FlirModule.disconnect();
553
- };
554
-
555
- const handleStartEmulator = async () => {
556
- try {
557
- await FlirModule.startEmulatorMode();
558
- Alert.alert('Success', 'Emulator mode started');
559
- } catch (error) {
560
- Alert.alert('Error', 'Failed to start emulator mode');
561
- }
562
- };
563
-
564
- const handleGetTemperature = async () => {
565
- try {
566
- // Get temperature at center of image (adjust coordinates as needed)
567
- const temp = await FlirModule.getTemperatureAt(160, 120);
568
-
569
- if (temp !== null) {
570
- setTemperature(temp);
571
- } else {
572
- Alert.alert('Info', 'No thermal data available');
573
- }
574
- } catch (error) {
575
- Alert.alert('Error', 'Failed to get temperature');
576
- }
577
- };
578
-
579
- return (
580
- <View style={styles.container}>
581
- <Text style={styles.title}>FLIR Thermal Camera</Text>
582
-
583
- <View style={styles.statusContainer}>
584
- <Text style={styles.statusLabel}>Status:</Text>
585
- <Text style={styles.statusValue}>
586
- {isConnected ? 'Connected' : isDiscovering ? 'Discovering...' : 'Disconnected'}
587
- </Text>
588
- </View>
589
-
590
- <View style={styles.statusContainer}>
591
- <Text style={styles.statusLabel}>Device:</Text>
592
- <Text style={styles.statusValue}>{deviceInfo}</Text>
593
- </View>
594
-
595
- {temperature !== null && (
596
- <View style={styles.statusContainer}>
597
- <Text style={styles.statusLabel}>Temperature:</Text>
598
- <Text style={styles.tempValue}>{temperature.toFixed(2)}°C</Text>
599
- </View>
600
- )}
601
-
602
- <View style={styles.buttonContainer}>
603
- {!isConnected ? (
604
- <>
605
- <Button
606
- title={isDiscovering ? 'Stop Discovery' : 'Start Discovery'}
607
- onPress={isDiscovering ? handleStopDiscovery : handleStartDiscovery}
608
- />
609
- <Button
610
- title="Start Emulator"
611
- onPress={handleStartEmulator}
612
- />
613
- </>
614
- ) : (
615
- <>
616
- <Button
617
- title="Get Temperature"
618
- onPress={handleGetTemperature}
619
- />
620
- <Button
621
- title="Disconnect"
622
- onPress={handleDisconnect}
623
- color="#d9534f"
624
- />
625
- </>
626
- )}
627
- </View>
628
-
629
- {isEmulator && (
630
- <Text style={styles.emulatorNote}>
631
- ℹ️ Running in emulator mode
632
- </Text>
633
- )}
634
- </View>
635
- );
636
- };
637
-
638
- const styles = StyleSheet.create({
639
- container: {
640
- flex: 1,
641
- padding: 20,
642
- backgroundColor: '#fff',
643
- },
644
- title: {
645
- fontSize: 24,
646
- fontWeight: 'bold',
647
- marginBottom: 20,
648
- },
649
- statusContainer: {
650
- flexDirection: 'row',
651
- marginBottom: 10,
652
- },
653
- statusLabel: {
654
- fontWeight: 'bold',
655
- width: 100,
656
- },
657
- statusValue: {
658
- flex: 1,
659
- },
660
- tempValue: {
661
- flex: 1,
662
- fontSize: 18,
663
- fontWeight: 'bold',
664
- color: '#f44336',
665
- },
666
- buttonContainer: {
667
- marginTop: 20,
668
- gap: 10,
669
- },
670
- emulatorNote: {
671
- marginTop: 20,
672
- fontStyle: 'italic',
673
- color: '#666',
674
- },
675
- });
676
-
677
- export default FlirThermalCamera;
678
- ```
679
-
680
- ### Step-by-Step Workflow
681
-
682
- #### 1. Initialize Event Listeners
683
-
684
- Always set up event listeners before starting discovery:
685
-
686
- ```javascript
687
- import { NativeModules, NativeEventEmitter } from 'react-native';
688
-
689
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
690
- const FlirEmitter = new NativeEventEmitter(FlirModule);
691
-
692
- // Listen for device connection
693
- FlirEmitter.addListener('FlirDeviceConnected', (event) => {
694
- console.log('Connected:', event);
695
- // event.identity - Device identity object
696
- // event.deviceType - "device" or "emulator"
697
- // event.isEmulator - boolean
698
- });
699
-
700
- // Listen for disconnection
701
- FlirEmitter.addListener('FlirDeviceDisconnected', (event) => {
702
- console.log('Disconnected:', event);
703
- });
704
-
705
- // Listen for errors
706
- FlirEmitter.addListener('FlirError', (event) => {
707
- console.error('Error:', event.error);
708
- // event.type - "discovery" or "connection"
709
- // event.interface - Communication interface
710
- });
711
- ```
712
-
713
- #### 2. Start Device Discovery
714
-
715
- ```javascript
716
- // Start scanning for FLIR devices
717
- FlirModule.startDiscovery();
718
-
719
- // Discovery will automatically emit events when devices are found
720
- // On Android: Scans for USB devices and emulators
721
- // On iOS: Scans for Lightning, BLE, and emulator devices
722
- ```
723
-
724
- #### 3. Handle Device Connection
725
-
726
- Devices connect automatically when discovered. You'll receive a `FlirDeviceConnected` event:
727
-
728
- ```javascript
729
- FlirEmitter.addListener('FlirDeviceConnected', async (event) => {
730
- // Device is now connected
731
- console.log('Device ID:', event.identity.deviceId);
732
- console.log('Is Emulator:', event.isEmulator);
733
-
734
- // Get additional device information
735
- const info = await FlirModule.getConnectedDeviceInfo();
736
- console.log('Device Info:', info);
737
-
738
- // Check connection status
739
- const connected = await FlirModule.isDeviceConnected();
740
- console.log('Is Connected:', connected);
741
- });
742
- ```
743
-
744
- #### 4. Measure Temperature
745
-
746
- Once connected, you can measure temperature at any point:
747
-
748
- ```javascript
749
- // Get temperature at pixel coordinates (x, y)
750
- const temp = await FlirModule.getTemperatureAt(160, 120);
751
-
752
- if (temp !== null) {
753
- console.log(`Temperature: ${temp.toFixed(2)}°C`);
754
- } else {
755
- console.log('No thermal data available');
756
- }
757
- ```
758
-
759
- **Important Notes**:
760
- - Coordinates are in pixels relative to the thermal image
761
- - Returns `null` if no thermal image is available
762
- - Temperature is in Celsius
763
- - For FLIR ONE: Thermal image is typically 160×120 pixels
764
- - For other cameras: Check device specifications
765
-
766
- #### 5. Disconnect
767
-
768
- ```javascript
769
- // Disconnect from current device
770
- FlirModule.disconnect();
771
-
772
- // This will trigger a FlirDeviceDisconnected event
773
- ```
774
-
775
- #### 6. Stop Discovery
776
-
777
- ```javascript
778
- // Stop scanning for devices
779
- FlirModule.stopDiscovery();
780
- ```
781
-
782
- ### Development Without Hardware (Emulator Mode)
783
-
784
- You can test your app without a physical FLIR device:
785
-
786
- ```javascript
787
- // Check if already in emulator mode
788
- const isEmu = await FlirModule.isEmulator();
789
-
790
- if (!isEmu) {
791
- // Force start emulator mode
792
- await FlirModule.startEmulatorMode();
793
- }
794
-
795
- // Emulator will provide simulated thermal data
796
- // All APIs work the same as with real hardware
797
- ```
798
-
799
- ### Best Practices
800
-
801
- #### 1. Always Clean Up Listeners
802
-
803
- ```javascript
804
- useEffect(() => {
805
- const listeners = [
806
- FlirEmitter.addListener('FlirDeviceConnected', handleConnect),
807
- FlirEmitter.addListener('FlirDeviceDisconnected', handleDisconnect),
808
- FlirEmitter.addListener('FlirError', handleError),
809
- ];
810
-
811
- return () => {
812
- // Remove all listeners on unmount
813
- listeners.forEach(listener => listener.remove());
814
-
815
- // Disconnect device
816
- FlirModule.disconnect();
817
- };
818
- }, []);
819
- ```
820
-
821
- #### 2. Handle Connection State
822
-
823
- ```javascript
824
- const [connectionState, setConnectionState] = useState('disconnected');
825
- // States: 'disconnected', 'discovering', 'connected'
826
-
827
- const handleStartDiscovery = () => {
828
- setConnectionState('discovering');
829
- FlirModule.startDiscovery();
830
- };
831
-
832
- FlirEmitter.addListener('FlirDeviceConnected', () => {
833
- setConnectionState('connected');
834
- });
835
-
836
- FlirEmitter.addListener('FlirDeviceDisconnected', () => {
837
- setConnectionState('disconnected');
838
- });
839
- ```
840
-
841
- #### 3. Error Handling
842
-
843
- ```javascript
844
- try {
845
- const temp = await FlirModule.getTemperatureAt(x, y);
846
-
847
- if (temp === null) {
848
- // No thermal data (device not streaming yet)
849
- console.log('Waiting for thermal data...');
850
- } else {
851
- // Valid temperature
852
- setTemperature(temp);
853
- }
854
- } catch (error) {
855
- console.error('Temperature measurement failed:', error);
856
- }
857
- ```
858
-
859
- #### 4. Temperature Sampling Rate
860
-
861
- Avoid calling `getTemperatureAt` too frequently:
862
-
863
- ```javascript
864
- // ❌ Bad: Calling too frequently
865
- setInterval(() => {
866
- FlirModule.getTemperatureAt(x, y);
867
- }, 16); // 60 FPS - too fast!
868
-
869
- // ✅ Good: Reasonable sampling rate
870
- setInterval(async () => {
871
- const temp = await FlirModule.getTemperatureAt(x, y);
872
- if (temp !== null) {
873
- setTemperature(temp);
874
- }
875
- }, 500); // 2 Hz - good for most applications
876
- ```
877
-
878
- ### Common Use Cases
879
-
880
- #### Use Case 1: Continuous Temperature Monitoring
881
-
882
- ```javascript
883
- const [centerTemp, setCenterTemp] = useState(null);
884
-
885
- useEffect(() => {
886
- if (!isConnected) return;
887
-
888
- // Poll temperature every 500ms
889
- const interval = setInterval(async () => {
890
- const temp = await FlirModule.getTemperatureAt(160, 120);
891
- if (temp !== null) {
892
- setCenterTemp(temp);
893
- }
894
- }, 500);
895
-
896
- return () => clearInterval(interval);
897
- }, [isConnected]);
898
- ```
899
-
900
- #### Use Case 2: Multi-Point Temperature Measurement
901
-
902
- ```javascript
903
- const measureMultiplePoints = async () => {
904
- const points = [
905
- { x: 80, y: 60, name: 'Top Left' },
906
- { x: 240, y: 60, name: 'Top Right' },
907
- { x: 160, y: 120, name: 'Center' },
908
- ];
909
-
910
- const results = await Promise.all(
911
- points.map(async (point) => {
912
- const temp = await FlirModule.getTemperatureAt(point.x, point.y);
913
- return { ...point, temperature: temp };
914
- })
915
- );
916
-
917
- console.log('Temperature readings:', results);
918
- return results;
919
- };
920
- ```
921
-
922
- #### Use Case 3: Auto-Connect on App Start
923
-
924
- ```javascript
925
- useEffect(() => {
926
- // Auto-start discovery when app loads
927
- FlirModule.startDiscovery();
928
-
929
- // Or use emulator if no device available
930
- setTimeout(async () => {
931
- const connected = await FlirModule.isDeviceConnected();
932
- if (!connected) {
933
- console.log('No device found, starting emulator');
934
- await FlirModule.startEmulatorMode();
935
- }
936
- }, 5000); // Wait 5 seconds for device
937
-
938
- return () => {
939
- FlirModule.stopDiscovery();
940
- FlirModule.disconnect();
941
- };
942
- }, []);
943
- ```
944
-
945
- ### Troubleshooting
946
-
947
- #### Problem: "No devices found"
948
-
949
- **Android**:
950
- - Ensure USB debugging is enabled
951
- - Check USB cable is data-capable (not charge-only)
952
- - Grant USB permissions when prompted
953
- - Try unplugging and replugging the FLIR device
954
-
955
- **iOS**:
956
- - Ensure Lightning connector is clean
957
- - Check Info.plist has External Accessory protocols
958
- - For BLE devices: Enable Bluetooth and grant permissions
959
- - Try force-quitting and restarting the app
960
-
961
- #### Problem: "Permission denied"
962
-
963
- **Android**:
964
- ```xml
965
- <!-- Ensure these are in AndroidManifest.xml (auto-added by library) -->
966
- <uses-feature android:name="android.hardware.usb.host" />
967
- <uses-permission android:name="android.permission.CAMERA"/>
968
- ```
969
-
970
- **iOS**:
971
- ```xml
972
- <!-- Ensure these are in Info.plist -->
973
- <key>NSBluetoothAlwaysUsageDescription</key>
974
- <string>Required for FLIR cameras</string>
975
- ```
976
-
977
- #### Problem: "Temperature returns null"
978
-
979
- ```javascript
980
- // Wait for device to start streaming
981
- FlirEmitter.addListener('FlirDeviceConnected', async (event) => {
982
- // Wait a moment for streaming to start
983
- setTimeout(async () => {
984
- const temp = await FlirModule.getTemperatureAt(160, 120);
985
- console.log('Temperature:', temp);
986
- }, 1000);
987
- });
988
- ```
989
-
990
- #### Problem: "App crashes on disconnect"
991
-
992
- ```javascript
993
- // Always check connection before API calls
994
- const getTemperatureSafely = async (x, y) => {
995
- const connected = await FlirModule.isDeviceConnected();
996
-
997
- if (!connected) {
998
- console.log('Device not connected');
999
- return null;
1000
- }
1001
-
1002
- return await FlirModule.getTemperatureAt(x, y);
1003
- };
1004
- ```
1005
-
1006
- #### Problem: "Events not firing"
1007
-
1008
- ```javascript
1009
- // Ensure NativeEventEmitter is created with the module
1010
- const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
1011
- const FlirEmitter = new NativeEventEmitter(FlirModule); // ✅ Pass module
1012
-
1013
- // ❌ Wrong:
1014
- const FlirEmitter = new NativeEventEmitter(); // No events will fire!
1015
- ```
1016
-
1017
- ### Platform-Specific Notes
1018
-
1019
- #### Android
1020
- - Supports USB FLIR ONE cameras
1021
- - Supports network-based FLIR cameras (ACE series)
1022
- - Requires physical device (emulator for development only)
1023
- - USB permissions handled automatically via `UsbPermissionHandler`
1024
-
1025
- #### iOS
1026
- - Supports Lightning interface (FLIR ONE Classic)
1027
- - Supports Bluetooth LE (FLIR ONE Edge/Pro)
1028
- - Works on both device and simulator (with emulator mode)
1029
- - Requires Info.plist entries (auto-added via config plugin)
1030
-
1031
- ### Performance Tips
1032
-
1033
- 1. **Limit temperature polling frequency**: 1-2 Hz is sufficient for most apps
1034
- 2. **Disconnect when not in use**: Save battery by disconnecting in background
1035
- 3. **Use emulator for UI development**: Build UI without physical hardware
1036
- 4. **Cache device info**: Don't call `getConnectedDeviceInfo()` repeatedly
1037
-
1038
- ## Publishing to JitPack
1039
-
1040
- To publish a new version to JitPack:
1041
-
1042
- 1. Commit all your changes:
1043
- ```bash
1044
- git add .
1045
- git commit -m "Release version 1.0.0"
1046
- ```
1047
-
1048
- 2. Create a git tag:
1049
- ```bash
1050
- git tag 1.0.0
1051
- git push origin 1.0.0
1052
- ```
1053
-
1054
- 3. JitPack will automatically build your library when someone requests it for the first time.
1055
-
1056
- ### JitPack / CI notes for local AAR dependencies
1057
-
1058
- If your module includes local AARs (for example the FLIR SDK binaries under `android/Flir/libs/`), CI environments such as JitPack will not automatically resolve file-based dependencies. To publish or build on JitPack you must ensure those AARs are available to the build system.
1059
-
1060
- Two options:
1061
- - Publish the AARs to a repository (mavenLocal or a remote Maven repo) before building the module.
1062
- - Bundle the AARs into the final AAR using a "fat-AAR" approach.
1063
-
1064
- The repository is configured to publish the FLIR SDK AARs into `mavenLocal` during the JitPack build (see `jitpack.yml`). That lets the `Flir` module resolve them by coordinates (`com.flir:thermalsdk:1.0.0` and `com.flir:androidsdk:1.0.0`) and prevents missing-class failures when JitPack builds the library.
1065
-
1066
- ### SLF4J duplicate-class conflict
1067
-
1068
- If you see build errors about duplicate classes in `org.slf4j.*` (for example `Duplicate class org.slf4j.Logger`), this happens when:
1069
-
1070
- - The vendor AAR (androidsdk/thermalsdk) embeds SLF4J classes inside the AAR's classes.jar;
1071
- - And your project or another dependency brings `org.slf4j:slf4j-api:...` as a separate jar. Gradle fails because the same classes exist twice.
1072
-
1073
- Two ways to resolve this without editing the vendor AAR:
1074
-
1075
- 1) Exclude SLF4J API from your build (preferred when vendor AAR bundles SLF4J classes):
1076
-
1077
- In your module's build.gradle.kts (or in the consuming app), add:
1078
-
1079
- ```kotlin
1080
- configurations.all {
1081
- exclude(group = "org.slf4j", module = "slf4j-api")
1082
- }
1083
- ```
1084
-
1085
- This prevents Gradle from pulling `slf4j-api` into the classpath and avoids duplicates.
1086
-
1087
- 2) Provide a single canonical SLF4J provider at runtime (if your app needs the slf4j API):
1088
-
1089
- Add a single SLF4J implementation/binding (for example `org.slf4j:slf4j-android` or an appropriate binding) and ensure other copies are excluded.
1090
-
1091
- Notes:
1092
- - We updated the Flir module to exclude `org.slf4j:slf4j-api` so it will not bring the API transitively. If you're still seeing duplicates in your app, check other dependencies and exclude slf4j there or use option 1.
1093
- - If you want me to, I can help you create a more robust fix (publishing Android AAR wrappers without embedded SLF4J or shading/relocating SLF4J) depending on your distribution needs.
1094
-
1095
- 4. Check build status at: `https://jitpack.io/#PraveenOjha/Flir`
1096
-
1097
- ### Notes for CI / JitPack
1098
-
1099
- If the Android build succeeds locally but fails on JitPack/CI with an error like:
1100
-
1101
- ```
1102
- Error: Unable to access jarfile /home/jitpack/build/gradle/wrapper/gradle-wrapper.jar
1103
- ```
1104
-
1105
- this typically means the Gradle wrapper files are missing from the published repository. JitPack runs builds in a clean VM and expects the wrapper files to be present in the repo. To make the build reproducible on JitPack, ensure you commit the following files:
1106
-
1107
- - `gradle/wrapper/gradle-wrapper.jar`
1108
- - `gradle/wrapper/gradle-wrapper.properties`
1109
- - `gradlew` (ensure executable bit is set)
1110
- - `gradlew.bat`
1111
-
1112
- After committing those files, trigger a new JitPack build (or push a new tag). Avoid committing `local.properties` — it contains developer-specific SDK paths and will break CI if present.
1113
-
1114
- ## Publishing to CocoaPods
1115
-
1116
- To publish to CocoaPods Trunk:
1117
-
1118
- 1. Register your CocoaPods account (first time only):
1119
- ```bash
1120
- pod trunk register your-email@example.com 'Your Name'
1121
- ```
1122
-
1123
- 2. Validate your podspec (run from repository root):
1124
- ```bash
1125
- pod spec lint ios/flir/Flir.podspec --allow-warnings
1126
- ```
1127
-
1128
- 3. Push to CocoaPods (run from repository root):
1129
- ```bash
1130
- pod trunk push ios/flir/Flir.podspec --allow-warnings
1131
- ```
1132
-
1133
- 4. Verify publication:
1134
- ```bash
1135
- pod search Flir
1136
- ```
1137
-
1138
- **Note:** The `--allow-warnings` flag may be needed for vendored frameworks.
1139
-
1140
- ## Development
1141
-
1142
- ### Building Locally
1143
-
1144
- #### Android
1145
-
1146
- ```bash
1147
- cd android
1148
- ./gradlew build
1149
- ```
1150
-
1151
- #### iOS
1152
-
1153
- ```bash
1154
- cd ios/flir
1155
- pod install
1156
- xcodebuild -workspace Flir.xcworkspace -scheme Flir -configuration Release
1157
- ```
1158
-
1159
- ### Testing
1160
-
1161
- ```bash
1162
- npm test
1163
- ```
1164
-
1165
- ## Emulator Mode
1166
-
1167
- This wrapper supports emulator mode for development and testing without requiring a physical FLIR device.
1168
-
1169
- ### Features
1170
-
1171
- - **Device Detection**: Automatically detect if running on an emulator
1172
- - **Fallback Mode**: Use FLIR's built-in emulator when no physical device is available
1173
- - **Consistent API**: Same API calls work for both emulator and physical devices
1174
-
1175
- ### Usage
1176
-
1177
- ```javascript
1178
- import FlirModule from 'react-native-flir';
1179
-
1180
- // Check if running in emulator mode
1181
- const isEmulator = await FlirModule.isEmulator();
1182
- console.log('Running in emulator:', isEmulator);
1183
-
1184
- // Check if a physical device is connected
1185
- const isDeviceConnected = await FlirModule.isDeviceConnected();
1186
- console.log('Physical device connected:', isDeviceConnected);
1187
-
1188
- // Get device information
1189
- const deviceInfo = await FlirModule.getConnectedDeviceInfo();
1190
- console.log('Device info:', deviceInfo);
1191
-
1192
- // Force start emulator mode (useful for testing)
1193
- await FlirModule.startEmulatorMode();
1194
- ```
1195
-
1196
- ### Emulator Features
1197
-
1198
- - **Simulated Thermal Data**: Provides mock thermal imaging data
1199
- - **Temperature Readings**: Returns simulated temperature values
1200
- - **Device Events**: Emits connection/disconnection events like physical devices
1201
- - **Testing Environment**: Perfect for CI/CD and development without hardware
1202
-
1203
- ### Android Emulator Detection
1204
-
1205
- The Android implementation detects emulators by checking:
1206
- - Build properties (`ro.build.fingerprint`, `ro.kernel.qemu`)
1207
- - Hardware characteristics
1208
- - Build model and manufacturer
1209
-
1210
- ### iOS Simulator Detection
1211
-
1212
- The iOS implementation uses:
1213
- - FLIR SDK's built-in emulator interface
1214
- - Runtime environment detection
1215
- - Simulator-specific device identifiers
1216
-
1217
- ## Requirements
1218
-
1219
- ### Android
1220
- - Android SDK 24+
1221
- - Kotlin 1.9.0+
1222
- - Gradle 8.1.0+
1223
- - Java 21
1224
-
1225
- ### iOS
1226
- - iOS 13.0+
1227
- - Xcode 14+
1228
- - CocoaPods 1.10+
1229
-
1230
- ## FLIR SDK Licensing
1231
-
1232
- This React Native wrapper is provided under the MIT license, but the FLIR thermal imaging SDKs have their own licensing requirements:
1233
-
1234
- 1. **Commercial Use**: Requires a commercial license from FLIR
1235
- 2. **Development License**: Required even for development and testing
1236
- 3. **Distribution**: You cannot distribute FLIR SDK libraries without proper licensing
1237
-
1238
- **Important**: The FLIR SDK libraries included in this repository are placeholders. You must:
1239
- - Register at [FLIR Developer Portal](https://www.flir.com/developer/mobile-sdk/)
1240
- - Download your licensed SDK versions
1241
- - Replace the placeholder files with your licensed libraries
1242
-
1243
- ## License
1244
-
1245
- **Wrapper Code**: MIT License (this React Native wrapper)
1246
- **FLIR SDK**: Proprietary license from FLIR Systems (see FLIR developer portal)
1247
-
1248
- By using this wrapper, you agree to comply with FLIR's licensing terms and conditions. License - see LICENSE file for details
1249
-
1250
- ### Disclaimer
1251
-
1252
- 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.
1253
-
1254
- ## Contributing
1255
-
1256
- Contributions are welcome! Please feel free to submit a Pull Request.
1257
-
1258
- ## Support
1259
-
1260
- For issues and questions:
1261
- - 🐛 [Report a bug](https://github.com/PraveenOjha/Flir/issues)
1262
- - 💡 [Request a feature](https://github.com/PraveenOjha/Flir/issues)
1263
- - 📖 [Documentation](https://github.com/PraveenOjha/Flir/wiki)
1264
-
1265
- ## Credits
1266
-
1267
- Built with the FLIR Thermal SDK
1268
-
1269
- ---
1270
-
1271
- Made with ❤️ by [Praveen Ojha](https://github.com/PraveenOjha)
1
+ # FLIR Thermal SDK - React Native
2
+
3
+ A React Native wrapper for the FLIR Thermal SDK, providing thermal imaging capabilities for both Android and iOS applications.
4
+
5
+ [![](https://jitpack.io/v/PraveenOjha/Flir.svg)](https://jitpack.io/#PraveenOjha/Flir)
6
+
7
+ ## Features
8
+
9
+ - 📱 Cross-platform support (Android & iOS)
10
+ - 🔥 Real-time thermal imaging
11
+ - 📸 Thermal image capture and processing
12
+ - 🎨 Customizable color palettes
13
+ - 📊 Temperature measurement and analysis
14
+ - ⚡ **Automatic permission setup** (no manual manifest/plist editing required)
15
+ - 📦 **On-demand SDK download** (~100MB downloaded only when needed)
16
+ - 🔌 USB & Bluetooth device support
17
+ - 🎮 Emulator mode for development without hardware
18
+
19
+ ## Installation
20
+
21
+ ### npm Installation
22
+
23
+ ```bash
24
+ npm install ilabs-flir
25
+ # or
26
+ yarn add ilabs-flir
27
+ ```
28
+
29
+ ### On-Demand SDK Download
30
+
31
+ The FLIR SDK binaries (~100MB) are **not bundled** with this package. They are downloaded on-demand when thermal features are first used.
32
+
33
+ #### Quick Start
34
+
35
+ ```typescript
36
+ import React, { useState, useEffect } from 'react';
37
+ import { View, Text, Button, ActivityIndicator } from 'react-native';
38
+ import { FlirDownload, FlirModule } from 'ilabs-flir';
39
+
40
+ function ThermalCamera() {
41
+ const [sdkReady, setSdkReady] = useState(false);
42
+ const [downloading, setDownloading] = useState(false);
43
+ const [progress, setProgress] = useState(0);
44
+
45
+ useEffect(() => {
46
+ checkAndDownloadSDK();
47
+ }, []);
48
+
49
+ const checkAndDownloadSDK = async () => {
50
+ try {
51
+ // Check if SDK is already available
52
+ const available = await FlirDownload.isAvailable();
53
+
54
+ if (available) {
55
+ setSdkReady(true);
56
+ return;
57
+ }
58
+
59
+ // Get download size to show user
60
+ const size = await FlirDownload.getDownloadSizeFormatted(); // "100 MB"
61
+ console.log(`SDK needs to be downloaded: ${size}`);
62
+
63
+ // Download with progress tracking
64
+ setDownloading(true);
65
+ await FlirDownload.download((progress) => {
66
+ setProgress(progress.percent);
67
+ console.log(`Downloading: ${progress.percent.toFixed(0)}%`);
68
+ });
69
+
70
+ setDownloading(false);
71
+ setSdkReady(true);
72
+ console.log('SDK ready!');
73
+ } catch (error) {
74
+ console.error('SDK download failed:', error);
75
+ setDownloading(false);
76
+ }
77
+ };
78
+
79
+ if (downloading) {
80
+ return (
81
+ <View>
82
+ <Text>Downloading FLIR SDK...</Text>
83
+ <Text>{progress.toFixed(0)}%</Text>
84
+ <ActivityIndicator size="large" />
85
+ </View>
86
+ );
87
+ }
88
+
89
+ if (!sdkReady) {
90
+ return (
91
+ <View>
92
+ <Text>FLIR SDK not available</Text>
93
+ <Button title="Download SDK" onPress={checkAndDownloadSDK} />
94
+ </View>
95
+ );
96
+ }
97
+
98
+ // SDK is ready, use FLIR features
99
+ return (
100
+ <View>
101
+ <Button title="Start Discovery" onPress={() => FlirModule.startDiscovery()} />
102
+ </View>
103
+ );
104
+ }
105
+ ```
106
+
107
+ #### FlirDownload API Reference
108
+
109
+ | Method | Returns | Description |
110
+ |--------|---------|-------------|
111
+ | `isAvailable()` | `Promise<boolean>` | Check if SDK is already downloaded |
112
+ | `getDownloadSize()` | `Promise<number>` | Get download size in bytes |
113
+ | `getDownloadSizeFormatted()` | `Promise<string>` | Get human-readable size (e.g., "100 MB") |
114
+ | `download(onProgress?)` | `Promise<void>` | Download SDK with optional progress callback |
115
+ | `cancel()` | `void` | Cancel ongoing download |
116
+ | `delete()` | `Promise<boolean>` | Remove downloaded SDK |
117
+
118
+ #### Progress Callback
119
+
120
+ ```typescript
121
+ await FlirDownload.download((progress) => {
122
+ console.log('Downloaded:', progress.bytesDownloaded);
123
+ console.log('Total:', progress.totalBytes);
124
+ console.log('Percent:', progress.percent); // 0-100
125
+ });
126
+ ```
127
+
128
+ #### Best Practices
129
+
130
+ **1. Download on First Launch**
131
+ ```typescript
132
+ // In your App.tsx or main component
133
+ useEffect(() => {
134
+ const initSDK = async () => {
135
+ const available = await FlirDownload.isAvailable();
136
+ if (!available) {
137
+ // Show a modal or screen explaining the download
138
+ await FlirDownload.download((progress) => {
139
+ updateProgressBar(progress.percent);
140
+ });
141
+ }
142
+ };
143
+ initSDK();
144
+ }, []);
145
+ ```
146
+
147
+ **2. Download Before Feature Access**
148
+ ```typescript
149
+ const openThermalCamera = async () => {
150
+ const available = await FlirDownload.isAvailable();
151
+
152
+ if (!available) {
153
+ Alert.alert(
154
+ 'Download Required',
155
+ 'FLIR SDK needs to be downloaded (100 MB). Continue?',
156
+ [
157
+ { text: 'Cancel', style: 'cancel' },
158
+ {
159
+ text: 'Download',
160
+ onPress: async () => {
161
+ await FlirDownload.download((p) => console.log(p.percent));
162
+ navigation.navigate('ThermalCamera');
163
+ }
164
+ }
165
+ ]
166
+ );
167
+ } else {
168
+ navigation.navigate('ThermalCamera');
169
+ }
170
+ };
171
+ ```
172
+
173
+ **3. Handle Errors**
174
+ ```typescript
175
+ try {
176
+ await FlirDownload.download((progress) => {
177
+ setProgress(progress.percent);
178
+ });
179
+ } catch (error) {
180
+ if (error.message.includes('Checksum')) {
181
+ Alert.alert('Download Error', 'File verification failed. Please try again.');
182
+ } else if (error.message.includes('Network')) {
183
+ Alert.alert('Network Error', 'Please check your internet connection.');
184
+ } else {
185
+ Alert.alert('Error', 'SDK download failed. Please try again.');
186
+ }
187
+ }
188
+ ```
189
+
190
+ **4. Show Download Size First**
191
+ ```typescript
192
+ const size = await FlirDownload.getDownloadSizeFormatted();
193
+ Alert.alert(
194
+ 'Download Required',
195
+ `FLIR SDK (${size}) needs to be downloaded. This is a one-time download.`,
196
+ [
197
+ { text: 'Cancel' },
198
+ { text: 'Download', onPress: () => downloadSDK() }
199
+ ]
200
+ );
201
+ ```
202
+
203
+ #### Where SDKs Are Downloaded From
204
+
205
+ - **Android**: GitHub Releases (with optional Google Play Feature Delivery support)
206
+ - **iOS**: GitHub Releases
207
+ - **Source**: https://github.com/PraveenOjha/flir-sdk-binaries/releases
208
+ - **Security**: SHA256 checksum verification on all downloads
209
+
210
+ #### Offline Usage
211
+
212
+ Once downloaded, the SDK is stored permanently on the device:
213
+ - **iOS**: `Application Support/FlirSDK/`
214
+ - **Android**: App's internal storage
215
+
216
+ No internet connection needed after initial download.
217
+
218
+ ### Manual SDK Download (Development)
219
+
220
+ For development, you can pre-download the SDK:
221
+
222
+ ```bash
223
+ npm run download-sdk ios
224
+ npm run download-sdk android
225
+ ```
226
+
227
+ ### Advanced: Google Play Feature Delivery (Android)
228
+
229
+ 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.
230
+
231
+ **Benefits:**
232
+ - ✅ Zero hosting and bandwidth costs (Google serves the files)
233
+ - ✅ Faster downloads via Google's CDN
234
+ - ✅ Automatic updates with your app
235
+ - ✅ Already implemented in the code!
236
+
237
+ **Setup:**
238
+
239
+ 1. Create a dynamic feature module in your Android project:
240
+ ```
241
+ android/
242
+ ├── app/
243
+ └── flir_sdk/ # New feature module
244
+ ├── build.gradle.kts
245
+ └── src/main/AndroidManifest.xml
246
+ ```
247
+
248
+ 2. See detailed setup guide: [PLAY_FEATURE_DELIVERY_GUIDE.md](./PLAY_FEATURE_DELIVERY_GUIDE.md)
249
+
250
+ 3. The code automatically uses Play Feature Delivery when available, with GitHub download as fallback.
251
+
252
+ **When to use:**
253
+ - ✅ Production apps on Google Play Store
254
+ - ✅ Want zero hosting costs
255
+ - ❌ Development builds (use GitHub download instead)
256
+ - ❌ Apps distributed outside Play Store (use GitHub download)
257
+
258
+
259
+ ### Prerequisites
260
+
261
+ - React Native 0.60+
262
+ - Android: minSdk 24, compileSdk 34
263
+ - iOS: iOS 13.0+
264
+
265
+ ### Quick Install
266
+
267
+ #### Android (via JitPack)
268
+
269
+ 1. Add JitPack repository to your root `build.gradle`:
270
+
271
+ ```gradle
272
+ allprojects {
273
+ repositories {
274
+ // ... other repositories
275
+ maven { url 'https://jitpack.io' }
276
+ }
277
+ }
278
+ ```
279
+
280
+ 2. Add the dependency to your app's `build.gradle`:
281
+
282
+ ```gradle
283
+ dependencies {
284
+ implementation 'com.github.PraveenOjha:Flir:1.0.0'
285
+ }
286
+ ```
287
+
288
+ 3. Sync your Gradle files.
289
+
290
+ **✅ Android permissions are automatically merged!** The library includes:
291
+ - USB host feature (for FLIR ONE USB devices)
292
+ - Camera & Internet permissions (for network-based FLIR cameras)
293
+
294
+ No manual `AndroidManifest.xml` editing required!
295
+
296
+ #### iOS (via CocoaPods)
297
+
298
+ 1. Add the following to your `Podfile`:
299
+
300
+ ```ruby
301
+ # From GitHub repository (recommended)
302
+ pod 'Flir', :git => 'https://github.com/PraveenOjha/Flir.git', :tag => '1.0.0'
303
+
304
+ # OR for local development
305
+ pod 'Flir', :podspec => '../path/to/Flir/Flir.podspec'
306
+ ```
307
+
308
+ 2. Run:
309
+
310
+ ```bash
311
+ cd ios
312
+ pod install
313
+ ```
314
+
315
+ 3. **Choose ONE of these options for iOS permissions:**
316
+
317
+ **Option A: Automatic (Recommended)** - Using React Native Config Plugin
318
+
319
+ Add to your `app.json`:
320
+
321
+ ```json
322
+ {
323
+ "plugins": ["ilabs-flir"]
324
+ }
325
+ ```
326
+
327
+ Then run:
328
+ ```bash
329
+ npx expo prebuild
330
+ ```
331
+
332
+ ✅ All Info.plist entries are **automatically added**!
333
+
334
+ **Option B: Manual Setup** - Add these entries to your `ios/YourApp/Info.plist`:
335
+
336
+ ```xml
337
+ <!-- External Accessory Protocols for FLIR ONE devices -->
338
+ <key>UISupportedExternalAccessoryProtocols</key>
339
+ <array>
340
+ <string>com.flir.rosebud.config</string>
341
+ <string>com.flir.rosebud.frame</string>
342
+ <string>com.flir.rosebud.fileio</string>
343
+ </array>
344
+
345
+ <!-- Bluetooth permissions for FLIR ONE Edge/Pro -->
346
+ <key>NSBluetoothAlwaysUsageDescription</key>
347
+ <string>This app requires Bluetooth to connect to FLIR thermal cameras</string>
348
+
349
+ <key>NSBluetoothPeripheralUsageDescription</key>
350
+ <string>This app uses Bluetooth to communicate with FLIR thermal imaging devices</string>
351
+ ```
352
+
353
+ ## Usage
354
+
355
+ ### Device Discovery
356
+
357
+ ```javascript
358
+ import { NativeModules, NativeEventEmitter } from 'react-native';
359
+
360
+ const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
361
+ const FlirEmitter = new NativeEventEmitter(FlirModule);
362
+
363
+ // Listen for device events
364
+ FlirEmitter.addListener('FlirDeviceConnected', (event) => {
365
+ console.log('FLIR device connected:', event);
366
+ });
367
+
368
+ FlirEmitter.addListener('FlirDeviceDisconnected', (event) => {
369
+ console.log('FLIR device disconnected:', event);
370
+ });
371
+
372
+ // Start discovering FLIR devices
373
+ FlirModule.startDiscovery();
374
+
375
+ // Stop discovery
376
+ FlirModule.stopDiscovery();
377
+ ```
378
+
379
+ ### Camera Connection
380
+
381
+ ```javascript
382
+ // Connect to discovered device
383
+ await FlirModule.connect(identityObject);
384
+
385
+ // Disconnect
386
+ FlirModule.disconnect();
387
+
388
+ // Check connection status
389
+ const isConnected = await FlirModule.isDeviceConnected();
390
+ const deviceInfo = await FlirModule.getConnectedDeviceInfo();
391
+ ```
392
+
393
+ ### Temperature Measurement
394
+
395
+ ```javascript
396
+ // Get temperature at specific point (x, y coordinates)
397
+ const temperature = await FlirModule.getTemperatureAt(100, 200);
398
+ console.log(`Temperature: ${temperature}°C`);
399
+
400
+ // Returns null if no thermal image is available
401
+ if (temperature !== null) {
402
+ console.log(`Detected: ${temperature.toFixed(2)}°C`);
403
+ }
404
+ ```
405
+
406
+ ### Emulator Mode (Development)
407
+
408
+ ```javascript
409
+ // Check if running in emulator mode
410
+ const isEmulator = await FlirModule.isEmulator();
411
+
412
+ // Force start emulator mode (for testing without hardware)
413
+ await FlirModule.startEmulatorMode();
414
+
415
+ // Get device information
416
+ const deviceInfo = await FlirModule.getConnectedDeviceInfo();
417
+ // Returns: "Emulator (FLIR ONE)" or "Physical device (FLIR ONE)"
418
+ ```
419
+
420
+ ## API Reference
421
+
422
+ ### Methods
423
+
424
+ | Method | Parameters | Returns | Description |
425
+ |--------|-----------|---------|-------------|
426
+ | **Device Discovery** |
427
+ | `startDiscovery()` | - | `void` | Start scanning forFLIR devices (USB & Emulator) |
428
+ | `stopDiscovery()` | - | `void` | Stop device discovery |
429
+ | **Connection** |
430
+ | `connect(identity)` | `identity: object` | `Promise<boolean>` | Connect to a discovered FLIR device |
431
+ | `disconnect()` | - | `void` | Disconnect from current device |
432
+ | `isDeviceConnected()` | - | `Promise<boolean>` | Check if a physical device is connected |
433
+ | `getConnectedDeviceInfo()` | - | `Promise<string>` | Get info about connected device |
434
+ | **Temperature** |
435
+ | `getTemperatureAt(x, y)` | `x: number, y: number` | `Promise<number \| null>` | Get temperature at pixel coordinates |
436
+ | **Emulator** |
437
+ | `isEmulator()` | - | `Promise<boolean>` | Check if running in emulator mode |
438
+ | `startEmulatorMode()` | - | `Promise<boolean>` | Force start emulator mode |
439
+
440
+ ### Events
441
+
442
+ Listen to these events using `NativeEventEmitter`:
443
+
444
+ | Event | Payload | Description |
445
+ |-------|---------|-------------|
446
+ | `FlirDeviceConnected` | `{ identity, deviceType, isEmulator }` | Fired when a FLIR device connects |
447
+ | `FlirDeviceDisconnected` | `{ identity, wasEmulator }` | Fired when a device disconnects |
448
+ | `FlirError` | `{ error, type, interface }` | Fired on discovery or connection errors |
449
+
450
+ ### Thermal Image Output
451
+
452
+ FLIR cameras provide two image types:
453
+ - **Thermal Image (MSX)**: Color-mapped thermal data with visual details
454
+ - **Photo Image (DC)**: Standard visible light image
455
+
456
+ **Color Palettes**: The SDK supports multiple palettes:
457
+ - `iron` - Rainbow color map (red=hot, blue=cold) - Default
458
+ - `gray` - Grayscale/black-white temperature map
459
+ - `arctic`, `rainbow`, etc. - Additional palettes
460
+
461
+ > **Note**: Palette switching requires accessing the native ThermalImage API directly. This may be exposed in future versions.
462
+
463
+ ## Detailed Usage Guide
464
+
465
+ ### Complete Setup Example
466
+
467
+ Here's a complete React Native component that demonstrates the full FLIR workflow:
468
+
469
+ ```javascript
470
+ import React, { useEffect, useState } from 'react';
471
+ import {
472
+ View,
473
+ Text,
474
+ Button,
475
+ StyleSheet,
476
+ NativeModules,
477
+ NativeEventEmitter,
478
+ Alert,
479
+ } from 'react-native';
480
+
481
+ const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
482
+ const FlirEmitter = new NativeEventEmitter(FlirModule);
483
+
484
+ const FlirThermalCamera = () => {
485
+ const [isDiscovering, setIsDiscovering] = useState(false);
486
+ const [isConnected, setIsConnected] = useState(false);
487
+ const [deviceInfo, setDeviceInfo] = useState('Not connected');
488
+ const [temperature, setTemperature] = useState(null);
489
+ const [isEmulator, setIsEmulator] = useState(false);
490
+
491
+ useEffect(() => {
492
+ // Set up event listeners
493
+ const deviceConnected = FlirEmitter.addListener(
494
+ 'FlirDeviceConnected',
495
+ (event) => {
496
+ console.log('Device connected:', event);
497
+ setIsConnected(true);
498
+ setIsDiscovering(false);
499
+
500
+ // Get device info after connection
501
+ FlirModule.getConnectedDeviceInfo().then(info => {
502
+ setDeviceInfo(info);
503
+ });
504
+ }
505
+ );
506
+
507
+ const deviceDisconnected = FlirEmitter.addListener(
508
+ 'FlirDeviceDisconnected',
509
+ (event) => {
510
+ console.log('Device disconnected:', event);
511
+ setIsConnected(false);
512
+ setDeviceInfo('Not connected');
513
+ setTemperature(null);
514
+ }
515
+ );
516
+
517
+ const deviceError = FlirEmitter.addListener(
518
+ 'FlirError',
519
+ (event) => {
520
+ console.error('FLIR Error:', event);
521
+ Alert.alert('FLIR Error', event.error || 'Unknown error');
522
+ }
523
+ );
524
+
525
+ // Check if we're in emulator mode
526
+ FlirModule.isEmulator().then(setIsEmulator);
527
+
528
+ // Cleanup listeners on unmount
529
+ return () => {
530
+ deviceConnected.remove();
531
+ deviceDisconnected.remove();
532
+ deviceError.remove();
533
+
534
+ // Disconnect on unmount
535
+ if (isConnected) {
536
+ FlirModule.disconnect();
537
+ }
538
+ };
539
+ }, []);
540
+
541
+ const handleStartDiscovery = () => {
542
+ setIsDiscovering(true);
543
+ FlirModule.startDiscovery();
544
+ };
545
+
546
+ const handleStopDiscovery = () => {
547
+ setIsDiscovering(false);
548
+ FlirModule.stopDiscovery();
549
+ };
550
+
551
+ const handleDisconnect = () => {
552
+ FlirModule.disconnect();
553
+ };
554
+
555
+ const handleStartEmulator = async () => {
556
+ try {
557
+ await FlirModule.startEmulatorMode();
558
+ Alert.alert('Success', 'Emulator mode started');
559
+ } catch (error) {
560
+ Alert.alert('Error', 'Failed to start emulator mode');
561
+ }
562
+ };
563
+
564
+ const handleGetTemperature = async () => {
565
+ try {
566
+ // Get temperature at center of image (adjust coordinates as needed)
567
+ const temp = await FlirModule.getTemperatureAt(160, 120);
568
+
569
+ if (temp !== null) {
570
+ setTemperature(temp);
571
+ } else {
572
+ Alert.alert('Info', 'No thermal data available');
573
+ }
574
+ } catch (error) {
575
+ Alert.alert('Error', 'Failed to get temperature');
576
+ }
577
+ };
578
+
579
+ return (
580
+ <View style={styles.container}>
581
+ <Text style={styles.title}>FLIR Thermal Camera</Text>
582
+
583
+ <View style={styles.statusContainer}>
584
+ <Text style={styles.statusLabel}>Status:</Text>
585
+ <Text style={styles.statusValue}>
586
+ {isConnected ? 'Connected' : isDiscovering ? 'Discovering...' : 'Disconnected'}
587
+ </Text>
588
+ </View>
589
+
590
+ <View style={styles.statusContainer}>
591
+ <Text style={styles.statusLabel}>Device:</Text>
592
+ <Text style={styles.statusValue}>{deviceInfo}</Text>
593
+ </View>
594
+
595
+ {temperature !== null && (
596
+ <View style={styles.statusContainer}>
597
+ <Text style={styles.statusLabel}>Temperature:</Text>
598
+ <Text style={styles.tempValue}>{temperature.toFixed(2)}°C</Text>
599
+ </View>
600
+ )}
601
+
602
+ <View style={styles.buttonContainer}>
603
+ {!isConnected ? (
604
+ <>
605
+ <Button
606
+ title={isDiscovering ? 'Stop Discovery' : 'Start Discovery'}
607
+ onPress={isDiscovering ? handleStopDiscovery : handleStartDiscovery}
608
+ />
609
+ <Button
610
+ title="Start Emulator"
611
+ onPress={handleStartEmulator}
612
+ />
613
+ </>
614
+ ) : (
615
+ <>
616
+ <Button
617
+ title="Get Temperature"
618
+ onPress={handleGetTemperature}
619
+ />
620
+ <Button
621
+ title="Disconnect"
622
+ onPress={handleDisconnect}
623
+ color="#d9534f"
624
+ />
625
+ </>
626
+ )}
627
+ </View>
628
+
629
+ {isEmulator && (
630
+ <Text style={styles.emulatorNote}>
631
+ ℹ️ Running in emulator mode
632
+ </Text>
633
+ )}
634
+ </View>
635
+ );
636
+ };
637
+
638
+ const styles = StyleSheet.create({
639
+ container: {
640
+ flex: 1,
641
+ padding: 20,
642
+ backgroundColor: '#fff',
643
+ },
644
+ title: {
645
+ fontSize: 24,
646
+ fontWeight: 'bold',
647
+ marginBottom: 20,
648
+ },
649
+ statusContainer: {
650
+ flexDirection: 'row',
651
+ marginBottom: 10,
652
+ },
653
+ statusLabel: {
654
+ fontWeight: 'bold',
655
+ width: 100,
656
+ },
657
+ statusValue: {
658
+ flex: 1,
659
+ },
660
+ tempValue: {
661
+ flex: 1,
662
+ fontSize: 18,
663
+ fontWeight: 'bold',
664
+ color: '#f44336',
665
+ },
666
+ buttonContainer: {
667
+ marginTop: 20,
668
+ gap: 10,
669
+ },
670
+ emulatorNote: {
671
+ marginTop: 20,
672
+ fontStyle: 'italic',
673
+ color: '#666',
674
+ },
675
+ });
676
+
677
+ export default FlirThermalCamera;
678
+ ```
679
+
680
+ ### Step-by-Step Workflow
681
+
682
+ #### 1. Initialize Event Listeners
683
+
684
+ Always set up event listeners before starting discovery:
685
+
686
+ ```javascript
687
+ import { NativeModules, NativeEventEmitter } from 'react-native';
688
+
689
+ const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
690
+ const FlirEmitter = new NativeEventEmitter(FlirModule);
691
+
692
+ // Listen for device connection
693
+ FlirEmitter.addListener('FlirDeviceConnected', (event) => {
694
+ console.log('Connected:', event);
695
+ // event.identity - Device identity object
696
+ // event.deviceType - "device" or "emulator"
697
+ // event.isEmulator - boolean
698
+ });
699
+
700
+ // Listen for disconnection
701
+ FlirEmitter.addListener('FlirDeviceDisconnected', (event) => {
702
+ console.log('Disconnected:', event);
703
+ });
704
+
705
+ // Listen for errors
706
+ FlirEmitter.addListener('FlirError', (event) => {
707
+ console.error('Error:', event.error);
708
+ // event.type - "discovery" or "connection"
709
+ // event.interface - Communication interface
710
+ });
711
+ ```
712
+
713
+ #### 2. Start Device Discovery
714
+
715
+ ```javascript
716
+ // Start scanning for FLIR devices
717
+ FlirModule.startDiscovery();
718
+
719
+ // Discovery will automatically emit events when devices are found
720
+ // On Android: Scans for USB devices and emulators
721
+ // On iOS: Scans for Lightning, BLE, and emulator devices
722
+ ```
723
+
724
+ #### 3. Handle Device Connection
725
+
726
+ Devices connect automatically when discovered. You'll receive a `FlirDeviceConnected` event:
727
+
728
+ ```javascript
729
+ FlirEmitter.addListener('FlirDeviceConnected', async (event) => {
730
+ // Device is now connected
731
+ console.log('Device ID:', event.identity.deviceId);
732
+ console.log('Is Emulator:', event.isEmulator);
733
+
734
+ // Get additional device information
735
+ const info = await FlirModule.getConnectedDeviceInfo();
736
+ console.log('Device Info:', info);
737
+
738
+ // Check connection status
739
+ const connected = await FlirModule.isDeviceConnected();
740
+ console.log('Is Connected:', connected);
741
+ });
742
+ ```
743
+
744
+ #### 4. Measure Temperature
745
+
746
+ Once connected, you can measure temperature at any point:
747
+
748
+ ```javascript
749
+ // Get temperature at pixel coordinates (x, y)
750
+ const temp = await FlirModule.getTemperatureAt(160, 120);
751
+
752
+ if (temp !== null) {
753
+ console.log(`Temperature: ${temp.toFixed(2)}°C`);
754
+ } else {
755
+ console.log('No thermal data available');
756
+ }
757
+ ```
758
+
759
+ **Important Notes**:
760
+ - Coordinates are in pixels relative to the thermal image
761
+ - Returns `null` if no thermal image is available
762
+ - Temperature is in Celsius
763
+ - For FLIR ONE: Thermal image is typically 160×120 pixels
764
+ - For other cameras: Check device specifications
765
+
766
+ #### 5. Disconnect
767
+
768
+ ```javascript
769
+ // Disconnect from current device
770
+ FlirModule.disconnect();
771
+
772
+ // This will trigger a FlirDeviceDisconnected event
773
+ ```
774
+
775
+ #### 6. Stop Discovery
776
+
777
+ ```javascript
778
+ // Stop scanning for devices
779
+ FlirModule.stopDiscovery();
780
+ ```
781
+
782
+ ### Development Without Hardware (Emulator Mode)
783
+
784
+ You can test your app without a physical FLIR device:
785
+
786
+ ```javascript
787
+ // Check if already in emulator mode
788
+ const isEmu = await FlirModule.isEmulator();
789
+
790
+ if (!isEmu) {
791
+ // Force start emulator mode
792
+ await FlirModule.startEmulatorMode();
793
+ }
794
+
795
+ // Emulator will provide simulated thermal data
796
+ // All APIs work the same as with real hardware
797
+ ```
798
+
799
+ ### Best Practices
800
+
801
+ #### 1. Always Clean Up Listeners
802
+
803
+ ```javascript
804
+ useEffect(() => {
805
+ const listeners = [
806
+ FlirEmitter.addListener('FlirDeviceConnected', handleConnect),
807
+ FlirEmitter.addListener('FlirDeviceDisconnected', handleDisconnect),
808
+ FlirEmitter.addListener('FlirError', handleError),
809
+ ];
810
+
811
+ return () => {
812
+ // Remove all listeners on unmount
813
+ listeners.forEach(listener => listener.remove());
814
+
815
+ // Disconnect device
816
+ FlirModule.disconnect();
817
+ };
818
+ }, []);
819
+ ```
820
+
821
+ #### 2. Handle Connection State
822
+
823
+ ```javascript
824
+ const [connectionState, setConnectionState] = useState('disconnected');
825
+ // States: 'disconnected', 'discovering', 'connected'
826
+
827
+ const handleStartDiscovery = () => {
828
+ setConnectionState('discovering');
829
+ FlirModule.startDiscovery();
830
+ };
831
+
832
+ FlirEmitter.addListener('FlirDeviceConnected', () => {
833
+ setConnectionState('connected');
834
+ });
835
+
836
+ FlirEmitter.addListener('FlirDeviceDisconnected', () => {
837
+ setConnectionState('disconnected');
838
+ });
839
+ ```
840
+
841
+ #### 3. Error Handling
842
+
843
+ ```javascript
844
+ try {
845
+ const temp = await FlirModule.getTemperatureAt(x, y);
846
+
847
+ if (temp === null) {
848
+ // No thermal data (device not streaming yet)
849
+ console.log('Waiting for thermal data...');
850
+ } else {
851
+ // Valid temperature
852
+ setTemperature(temp);
853
+ }
854
+ } catch (error) {
855
+ console.error('Temperature measurement failed:', error);
856
+ }
857
+ ```
858
+
859
+ #### 4. Temperature Sampling Rate
860
+
861
+ Avoid calling `getTemperatureAt` too frequently:
862
+
863
+ ```javascript
864
+ // ❌ Bad: Calling too frequently
865
+ setInterval(() => {
866
+ FlirModule.getTemperatureAt(x, y);
867
+ }, 16); // 60 FPS - too fast!
868
+
869
+ // ✅ Good: Reasonable sampling rate
870
+ setInterval(async () => {
871
+ const temp = await FlirModule.getTemperatureAt(x, y);
872
+ if (temp !== null) {
873
+ setTemperature(temp);
874
+ }
875
+ }, 500); // 2 Hz - good for most applications
876
+ ```
877
+
878
+ ### Common Use Cases
879
+
880
+ #### Use Case 1: Continuous Temperature Monitoring
881
+
882
+ ```javascript
883
+ const [centerTemp, setCenterTemp] = useState(null);
884
+
885
+ useEffect(() => {
886
+ if (!isConnected) return;
887
+
888
+ // Poll temperature every 500ms
889
+ const interval = setInterval(async () => {
890
+ const temp = await FlirModule.getTemperatureAt(160, 120);
891
+ if (temp !== null) {
892
+ setCenterTemp(temp);
893
+ }
894
+ }, 500);
895
+
896
+ return () => clearInterval(interval);
897
+ }, [isConnected]);
898
+ ```
899
+
900
+ #### Use Case 2: Multi-Point Temperature Measurement
901
+
902
+ ```javascript
903
+ const measureMultiplePoints = async () => {
904
+ const points = [
905
+ { x: 80, y: 60, name: 'Top Left' },
906
+ { x: 240, y: 60, name: 'Top Right' },
907
+ { x: 160, y: 120, name: 'Center' },
908
+ ];
909
+
910
+ const results = await Promise.all(
911
+ points.map(async (point) => {
912
+ const temp = await FlirModule.getTemperatureAt(point.x, point.y);
913
+ return { ...point, temperature: temp };
914
+ })
915
+ );
916
+
917
+ console.log('Temperature readings:', results);
918
+ return results;
919
+ };
920
+ ```
921
+
922
+ #### Use Case 3: Auto-Connect on App Start
923
+
924
+ ```javascript
925
+ useEffect(() => {
926
+ // Auto-start discovery when app loads
927
+ FlirModule.startDiscovery();
928
+
929
+ // Or use emulator if no device available
930
+ setTimeout(async () => {
931
+ const connected = await FlirModule.isDeviceConnected();
932
+ if (!connected) {
933
+ console.log('No device found, starting emulator');
934
+ await FlirModule.startEmulatorMode();
935
+ }
936
+ }, 5000); // Wait 5 seconds for device
937
+
938
+ return () => {
939
+ FlirModule.stopDiscovery();
940
+ FlirModule.disconnect();
941
+ };
942
+ }, []);
943
+ ```
944
+
945
+ ### Troubleshooting
946
+
947
+ #### Problem: "No devices found"
948
+
949
+ **Android**:
950
+ - Ensure USB debugging is enabled
951
+ - Check USB cable is data-capable (not charge-only)
952
+ - Grant USB permissions when prompted
953
+ - Try unplugging and replugging the FLIR device
954
+
955
+ **iOS**:
956
+ - Ensure Lightning connector is clean
957
+ - Check Info.plist has External Accessory protocols
958
+ - For BLE devices: Enable Bluetooth and grant permissions
959
+ - Try force-quitting and restarting the app
960
+
961
+ #### Problem: "Permission denied"
962
+
963
+ **Android**:
964
+ ```xml
965
+ <!-- Ensure these are in AndroidManifest.xml (auto-added by library) -->
966
+ <uses-feature android:name="android.hardware.usb.host" />
967
+ <uses-permission android:name="android.permission.CAMERA"/>
968
+ ```
969
+
970
+ **iOS**:
971
+ ```xml
972
+ <!-- Ensure these are in Info.plist -->
973
+ <key>NSBluetoothAlwaysUsageDescription</key>
974
+ <string>Required for FLIR cameras</string>
975
+ ```
976
+
977
+ #### Problem: "Temperature returns null"
978
+
979
+ ```javascript
980
+ // Wait for device to start streaming
981
+ FlirEmitter.addListener('FlirDeviceConnected', async (event) => {
982
+ // Wait a moment for streaming to start
983
+ setTimeout(async () => {
984
+ const temp = await FlirModule.getTemperatureAt(160, 120);
985
+ console.log('Temperature:', temp);
986
+ }, 1000);
987
+ });
988
+ ```
989
+
990
+ #### Problem: "App crashes on disconnect"
991
+
992
+ ```javascript
993
+ // Always check connection before API calls
994
+ const getTemperatureSafely = async (x, y) => {
995
+ const connected = await FlirModule.isDeviceConnected();
996
+
997
+ if (!connected) {
998
+ console.log('Device not connected');
999
+ return null;
1000
+ }
1001
+
1002
+ return await FlirModule.getTemperatureAt(x, y);
1003
+ };
1004
+ ```
1005
+
1006
+ #### Problem: "Events not firing"
1007
+
1008
+ ```javascript
1009
+ // Ensure NativeEventEmitter is created with the module
1010
+ const FlirModule = NativeModules.FlirIOS || NativeModules.FlirAndroid;
1011
+ const FlirEmitter = new NativeEventEmitter(FlirModule); // ✅ Pass module
1012
+
1013
+ // ❌ Wrong:
1014
+ const FlirEmitter = new NativeEventEmitter(); // No events will fire!
1015
+ ```
1016
+
1017
+ ### Platform-Specific Notes
1018
+
1019
+ #### Android
1020
+ - Supports USB FLIR ONE cameras
1021
+ - Supports network-based FLIR cameras (ACE series)
1022
+ - Requires physical device (emulator for development only)
1023
+ - USB permissions handled automatically via `UsbPermissionHandler`
1024
+
1025
+ #### iOS
1026
+ - Supports Lightning interface (FLIR ONE Classic)
1027
+ - Supports Bluetooth LE (FLIR ONE Edge/Pro)
1028
+ - Works on both device and simulator (with emulator mode)
1029
+ - Requires Info.plist entries (auto-added via config plugin)
1030
+
1031
+ ### Performance Tips
1032
+
1033
+ 1. **Limit temperature polling frequency**: 1-2 Hz is sufficient for most apps
1034
+ 2. **Disconnect when not in use**: Save battery by disconnecting in background
1035
+ 3. **Use emulator for UI development**: Build UI without physical hardware
1036
+ 4. **Cache device info**: Don't call `getConnectedDeviceInfo()` repeatedly
1037
+
1038
+ ## Publishing to JitPack
1039
+
1040
+ To publish a new version to JitPack:
1041
+
1042
+ 1. Commit all your changes:
1043
+ ```bash
1044
+ git add .
1045
+ git commit -m "Release version 1.0.0"
1046
+ ```
1047
+
1048
+ 2. Create a git tag:
1049
+ ```bash
1050
+ git tag 1.0.0
1051
+ git push origin 1.0.0
1052
+ ```
1053
+
1054
+ 3. JitPack will automatically build your library when someone requests it for the first time.
1055
+
1056
+ ### JitPack / CI notes for local AAR dependencies
1057
+
1058
+ If your module includes local AARs (for example the FLIR SDK binaries under `android/Flir/libs/`), CI environments such as JitPack will not automatically resolve file-based dependencies. To publish or build on JitPack you must ensure those AARs are available to the build system.
1059
+
1060
+ Two options:
1061
+ - Publish the AARs to a repository (mavenLocal or a remote Maven repo) before building the module.
1062
+ - Bundle the AARs into the final AAR using a "fat-AAR" approach.
1063
+
1064
+ The repository is configured to publish the FLIR SDK AARs into `mavenLocal` during the JitPack build (see `jitpack.yml`). That lets the `Flir` module resolve them by coordinates (`com.flir:thermalsdk:1.0.0` and `com.flir:androidsdk:1.0.0`) and prevents missing-class failures when JitPack builds the library.
1065
+
1066
+ ### SLF4J duplicate-class conflict
1067
+
1068
+ If you see build errors about duplicate classes in `org.slf4j.*` (for example `Duplicate class org.slf4j.Logger`), this happens when:
1069
+
1070
+ - The vendor AAR (androidsdk/thermalsdk) embeds SLF4J classes inside the AAR's classes.jar;
1071
+ - And your project or another dependency brings `org.slf4j:slf4j-api:...` as a separate jar. Gradle fails because the same classes exist twice.
1072
+
1073
+ Two ways to resolve this without editing the vendor AAR:
1074
+
1075
+ 1) Exclude SLF4J API from your build (preferred when vendor AAR bundles SLF4J classes):
1076
+
1077
+ In your module's build.gradle.kts (or in the consuming app), add:
1078
+
1079
+ ```kotlin
1080
+ configurations.all {
1081
+ exclude(group = "org.slf4j", module = "slf4j-api")
1082
+ }
1083
+ ```
1084
+
1085
+ This prevents Gradle from pulling `slf4j-api` into the classpath and avoids duplicates.
1086
+
1087
+ 2) Provide a single canonical SLF4J provider at runtime (if your app needs the slf4j API):
1088
+
1089
+ Add a single SLF4J implementation/binding (for example `org.slf4j:slf4j-android` or an appropriate binding) and ensure other copies are excluded.
1090
+
1091
+ Notes:
1092
+ - We updated the Flir module to exclude `org.slf4j:slf4j-api` so it will not bring the API transitively. If you're still seeing duplicates in your app, check other dependencies and exclude slf4j there or use option 1.
1093
+ - If you want me to, I can help you create a more robust fix (publishing Android AAR wrappers without embedded SLF4J or shading/relocating SLF4J) depending on your distribution needs.
1094
+
1095
+ 4. Check build status at: `https://jitpack.io/#PraveenOjha/Flir`
1096
+
1097
+ ### Notes for CI / JitPack
1098
+
1099
+ If the Android build succeeds locally but fails on JitPack/CI with an error like:
1100
+
1101
+ ```
1102
+ Error: Unable to access jarfile /home/jitpack/build/gradle/wrapper/gradle-wrapper.jar
1103
+ ```
1104
+
1105
+ this typically means the Gradle wrapper files are missing from the published repository. JitPack runs builds in a clean VM and expects the wrapper files to be present in the repo. To make the build reproducible on JitPack, ensure you commit the following files:
1106
+
1107
+ - `gradle/wrapper/gradle-wrapper.jar`
1108
+ - `gradle/wrapper/gradle-wrapper.properties`
1109
+ - `gradlew` (ensure executable bit is set)
1110
+ - `gradlew.bat`
1111
+
1112
+ After committing those files, trigger a new JitPack build (or push a new tag). Avoid committing `local.properties` — it contains developer-specific SDK paths and will break CI if present.
1113
+
1114
+ ## Publishing to CocoaPods
1115
+
1116
+ To publish to CocoaPods Trunk:
1117
+
1118
+ 1. Register your CocoaPods account (first time only):
1119
+ ```bash
1120
+ pod trunk register your-email@example.com 'Your Name'
1121
+ ```
1122
+
1123
+ 2. Validate your podspec (run from repository root):
1124
+ ```bash
1125
+ pod spec lint ios/flir/Flir.podspec --allow-warnings
1126
+ ```
1127
+
1128
+ 3. Push to CocoaPods (run from repository root):
1129
+ ```bash
1130
+ pod trunk push ios/flir/Flir.podspec --allow-warnings
1131
+ ```
1132
+
1133
+ 4. Verify publication:
1134
+ ```bash
1135
+ pod search Flir
1136
+ ```
1137
+
1138
+ **Note:** The `--allow-warnings` flag may be needed for vendored frameworks.
1139
+
1140
+ ## Development
1141
+
1142
+ ### Building Locally
1143
+
1144
+ #### Android
1145
+
1146
+ ```bash
1147
+ cd android
1148
+ ./gradlew build
1149
+ ```
1150
+
1151
+ #### iOS
1152
+
1153
+ ```bash
1154
+ cd ios/flir
1155
+ pod install
1156
+ xcodebuild -workspace Flir.xcworkspace -scheme Flir -configuration Release
1157
+ ```
1158
+
1159
+ ### Testing
1160
+
1161
+ ```bash
1162
+ npm test
1163
+ ```
1164
+
1165
+ ## Emulator Mode
1166
+
1167
+ This wrapper supports emulator mode for development and testing without requiring a physical FLIR device.
1168
+
1169
+ ### Features
1170
+
1171
+ - **Device Detection**: Automatically detect if running on an emulator
1172
+ - **Fallback Mode**: Use FLIR's built-in emulator when no physical device is available
1173
+ - **Consistent API**: Same API calls work for both emulator and physical devices
1174
+
1175
+ ### Usage
1176
+
1177
+ ```javascript
1178
+ import FlirModule from 'react-native-flir';
1179
+
1180
+ // Check if running in emulator mode
1181
+ const isEmulator = await FlirModule.isEmulator();
1182
+ console.log('Running in emulator:', isEmulator);
1183
+
1184
+ // Check if a physical device is connected
1185
+ const isDeviceConnected = await FlirModule.isDeviceConnected();
1186
+ console.log('Physical device connected:', isDeviceConnected);
1187
+
1188
+ // Get device information
1189
+ const deviceInfo = await FlirModule.getConnectedDeviceInfo();
1190
+ console.log('Device info:', deviceInfo);
1191
+
1192
+ // Force start emulator mode (useful for testing)
1193
+ await FlirModule.startEmulatorMode();
1194
+ ```
1195
+
1196
+ ### Emulator Features
1197
+
1198
+ - **Simulated Thermal Data**: Provides mock thermal imaging data
1199
+ - **Temperature Readings**: Returns simulated temperature values
1200
+ - **Device Events**: Emits connection/disconnection events like physical devices
1201
+ - **Testing Environment**: Perfect for CI/CD and development without hardware
1202
+
1203
+ ### Android Emulator Detection
1204
+
1205
+ The Android implementation detects emulators by checking:
1206
+ - Build properties (`ro.build.fingerprint`, `ro.kernel.qemu`)
1207
+ - Hardware characteristics
1208
+ - Build model and manufacturer
1209
+
1210
+ ### iOS Simulator Detection
1211
+
1212
+ The iOS implementation uses:
1213
+ - FLIR SDK's built-in emulator interface
1214
+ - Runtime environment detection
1215
+ - Simulator-specific device identifiers
1216
+
1217
+ ## Requirements
1218
+
1219
+ ### Android
1220
+ - Android SDK 24+
1221
+ - Kotlin 1.9.0+
1222
+ - Gradle 8.1.0+
1223
+ - Java 21
1224
+
1225
+ ### iOS
1226
+ - iOS 13.0+
1227
+ - Xcode 14+
1228
+ - CocoaPods 1.10+
1229
+
1230
+ ## FLIR SDK Licensing
1231
+
1232
+ This React Native wrapper is provided under the MIT license, but the FLIR thermal imaging SDKs have their own licensing requirements:
1233
+
1234
+ 1. **Commercial Use**: Requires a commercial license from FLIR
1235
+ 2. **Development License**: Required even for development and testing
1236
+ 3. **Distribution**: You cannot distribute FLIR SDK libraries without proper licensing
1237
+
1238
+ **Important**: The FLIR SDK libraries included in this repository are placeholders. You must:
1239
+ - Register at [FLIR Developer Portal](https://www.flir.com/developer/mobile-sdk/)
1240
+ - Download your licensed SDK versions
1241
+ - Replace the placeholder files with your licensed libraries
1242
+
1243
+ ## License
1244
+
1245
+ **Wrapper Code**: MIT License (this React Native wrapper)
1246
+ **FLIR SDK**: Proprietary license from FLIR Systems (see FLIR developer portal)
1247
+
1248
+ By using this wrapper, you agree to comply with FLIR's licensing terms and conditions. License - see LICENSE file for details
1249
+
1250
+ ### Disclaimer
1251
+
1252
+ 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.
1253
+
1254
+ ## Contributing
1255
+
1256
+ Contributions are welcome! Please feel free to submit a Pull Request.
1257
+
1258
+ ## Support
1259
+
1260
+ For issues and questions:
1261
+ - 🐛 [Report a bug](https://github.com/PraveenOjha/Flir/issues)
1262
+ - 💡 [Request a feature](https://github.com/PraveenOjha/Flir/issues)
1263
+ - 📖 [Documentation](https://github.com/PraveenOjha/Flir/wiki)
1264
+
1265
+ ## Credits
1266
+
1267
+ Built with the FLIR Thermal SDK
1268
+
1269
+ ---
1270
+
1271
+ Made with ❤️ by [Praveen Ojha](https://github.com/PraveenOjha)