ilabs-flir 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/Flir.podspec +31 -0
  2. package/README.md +1271 -0
  3. package/android/Flir/build.gradle.kts +80 -0
  4. package/android/Flir/libs/flir-stubs.jar +0 -0
  5. package/android/Flir/src/main/AndroidManifest.xml +31 -0
  6. package/android/Flir/src/main/java/flir/android/CameraHandler.java +194 -0
  7. package/android/Flir/src/main/java/flir/android/FlirController.kt +11 -0
  8. package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +75 -0
  9. package/android/Flir/src/main/java/flir/android/FlirDownloadPackage.kt +16 -0
  10. package/android/Flir/src/main/java/flir/android/FlirFrameCache.kt +6 -0
  11. package/android/Flir/src/main/java/flir/android/FlirManager.kt +248 -0
  12. package/android/Flir/src/main/java/flir/android/FlirModule.kt +74 -0
  13. package/android/Flir/src/main/java/flir/android/FlirPackage.kt +16 -0
  14. package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +191 -0
  15. package/android/Flir/src/main/java/flir/android/FlirStatus.kt +12 -0
  16. package/android/Flir/src/main/java/flir/android/FlirView.kt +48 -0
  17. package/android/Flir/src/main/java/flir/android/FlirViewManager.kt +13 -0
  18. package/android/Flir/src/main/java/flir/android/FrameDataHolder.java +14 -0
  19. package/app.plugin.js +264 -0
  20. package/expo-module.config.json +6 -0
  21. package/ios/Flir/Framework/ThermalSDK/FLIRBattery.h +76 -0
  22. package/ios/Flir/Framework/ThermalSDK/FLIRCalibration.h +108 -0
  23. package/ios/Flir/Framework/ThermalSDK/FLIRCamera.h +156 -0
  24. package/ios/Flir/Framework/ThermalSDK/FLIRCameraDeviceInfo.h +53 -0
  25. package/ios/Flir/Framework/ThermalSDK/FLIRCameraEvent.h +132 -0
  26. package/ios/Flir/Framework/ThermalSDK/FLIRCameraImport.h +204 -0
  27. package/ios/Flir/Framework/ThermalSDK/FLIRColorDistributionSettings.h +204 -0
  28. package/ios/Flir/Framework/ThermalSDK/FLIRColorizer.h +82 -0
  29. package/ios/Flir/Framework/ThermalSDK/FLIRDiscoveredCamera.h +44 -0
  30. package/ios/Flir/Framework/ThermalSDK/FLIRDiscovery.h +132 -0
  31. package/ios/Flir/Framework/ThermalSDK/FLIRDisplaySettings.h +29 -0
  32. package/ios/Flir/Framework/ThermalSDK/FLIRFocus.h +70 -0
  33. package/ios/Flir/Framework/ThermalSDK/FLIRFusion.h +192 -0
  34. package/ios/Flir/Framework/ThermalSDK/FLIRFusionController.h +136 -0
  35. package/ios/Flir/Framework/ThermalSDK/FLIRFusionTransformation.h +35 -0
  36. package/ios/Flir/Framework/ThermalSDK/FLIRIdentity.h +264 -0
  37. package/ios/Flir/Framework/ThermalSDK/FLIRImageBase.h +196 -0
  38. package/ios/Flir/Framework/ThermalSDK/FLIRImageColorizer.h +26 -0
  39. package/ios/Flir/Framework/ThermalSDK/FLIRImageStatistics.h +61 -0
  40. package/ios/Flir/Framework/ThermalSDK/FLIRIsotherms.h +208 -0
  41. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementArea.h +38 -0
  42. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementCollection.h +147 -0
  43. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDelta.h +62 -0
  44. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDimensions.h +33 -0
  45. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementEllipse.h +49 -0
  46. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementLine.h +66 -0
  47. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementMarker.h +69 -0
  48. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementParameters.h +41 -0
  49. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementRectangle.h +36 -0
  50. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementReference.h +27 -0
  51. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementShape.h +46 -0
  52. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementSpot.h +33 -0
  53. package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementsController.h +160 -0
  54. package/ios/Flir/Framework/ThermalSDK/FLIRMeterLinkSensorPoll.h +247 -0
  55. package/ios/Flir/Framework/ThermalSDK/FLIROverlayController.h +27 -0
  56. package/ios/Flir/Framework/ThermalSDK/FLIRPalette.h +60 -0
  57. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteController.h +36 -0
  58. package/ios/Flir/Framework/ThermalSDK/FLIRPaletteManager.h +97 -0
  59. package/ios/Flir/Framework/ThermalSDK/FLIRQuantification.h +55 -0
  60. package/ios/Flir/Framework/ThermalSDK/FLIRRemoteControl.h +393 -0
  61. package/ios/Flir/Framework/ThermalSDK/FLIRRenderer.h +35 -0
  62. package/ios/Flir/Framework/ThermalSDK/FLIRRendererImpl.h +17 -0
  63. package/ios/Flir/Framework/ThermalSDK/FLIRScale.h +99 -0
  64. package/ios/Flir/Framework/ThermalSDK/FLIRScaleController.h +44 -0
  65. package/ios/Flir/Framework/ThermalSDK/FLIRStream.h +109 -0
  66. package/ios/Flir/Framework/ThermalSDK/FLIRStreamer.h +124 -0
  67. package/ios/Flir/Framework/ThermalSDK/FLIRSystem.h +40 -0
  68. package/ios/Flir/Framework/ThermalSDK/FLIRTemperatureRange.h +43 -0
  69. package/ios/Flir/Framework/ThermalSDK/FLIRThermalDelta.h +77 -0
  70. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImage.h +331 -0
  71. package/ios/Flir/Framework/ThermalSDK/FLIRThermalImageFile.h +56 -0
  72. package/ios/Flir/Framework/ThermalSDK/FLIRThermalParameters.h +31 -0
  73. package/ios/Flir/Framework/ThermalSDK/FLIRThermalValue.h +92 -0
  74. package/ios/Flir/Framework/ThermalSDK/FLIRWirelessCameraDetails.h +88 -0
  75. package/ios/Flir/Framework/ThermalSDK/ThermalSDK.h +73 -0
  76. package/ios/Flir/SDKLoader/FlirSDKLoader.m +13 -0
  77. package/ios/Flir/SDKLoader/FlirSDKLoader.swift +175 -0
  78. package/ios/Flir/src/FlirEventEmitter.h +12 -0
  79. package/ios/Flir/src/FlirEventEmitter.m +33 -0
  80. package/ios/Flir/src/FlirModule.h +10 -0
  81. package/ios/Flir/src/FlirModule.m +381 -0
  82. package/ios/Flir/src/FlirPreviewView.h +13 -0
  83. package/ios/Flir/src/FlirPreviewView.m +24 -0
  84. package/ios/Flir/src/FlirState.h +20 -0
  85. package/ios/Flir/src/FlirState.m +79 -0
  86. package/ios/Flir/src/FlirViewManager.h +9 -0
  87. package/ios/Flir/src/FlirViewManager.m +16 -0
  88. package/package.json +61 -0
  89. package/react-native.config.js +14 -0
  90. package/scripts/copy_ios_libs.sh +32 -0
  91. package/scripts/download-sdk.js +62 -0
  92. package/scripts/prepare-binaries.sh +171 -0
  93. package/sdk-manifest.json +30 -0
  94. package/src/FlirDownload.ts +78 -0
  95. package/src/index.d.ts +17 -0
  96. package/src/index.js +7 -0
  97. package/src/index.ts +7 -0
@@ -0,0 +1,381 @@
1
+ #import "FlirModule.h"
2
+ #import "FlirEventEmitter.h"
3
+ #import "FlirState.h"
4
+ #import <ThermalSDK/ThermalSDK.h>
5
+ #import <React/RCTLog.h>
6
+
7
+ #ifndef F1_gen3
8
+ #define F1_gen3 FLIRCameraType_flirOne
9
+ #endif
10
+
11
+
12
+ @interface FlirModule() <FLIRDiscoveryEventDelegate>
13
+ @property (nonatomic, strong) FLIRDiscovery *discovery;
14
+ @property (nonatomic, strong) FLIRCamera *camera;
15
+ @property (nonatomic, strong) FLIRIdentity *connectedIdentity;
16
+ @property (nonatomic, assign) BOOL isEmulatorMode;
17
+ @property (nonatomic, assign) BOOL isPhysicalDeviceConnected;
18
+ @end
19
+
20
+ @implementation FlirModule
21
+
22
+ RCT_EXPORT_MODULE(FlirIOS);
23
+
24
+ RCT_EXPORT_METHOD(startDiscovery)
25
+ {
26
+ // Hook into ThermalSDK discovery when ready. For now, emit an event to JS.
27
+ dispatch_async(dispatch_get_main_queue(), ^{
28
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{ @"msg": @"discovery-started" }];
29
+ RCTLogInfo(@"FLIR discovery started (placeholder)");
30
+ });
31
+ }
32
+
33
+ RCT_EXPORT_METHOD(stopDiscovery)
34
+ {
35
+ dispatch_async(dispatch_get_main_queue(), ^{
36
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{ @"msg": @"discovery-stopped" }];
37
+ RCTLogInfo(@"FLIR discovery stopped (placeholder)");
38
+ });
39
+ }
40
+
41
+ RCT_EXPORT_METHOD(connect:(NSDictionary *)identity resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
42
+ {
43
+ // TODO: implement real connect using ThermalSDK and identity info
44
+ dispatch_async(dispatch_get_main_queue(), ^{
45
+ RCTLogInfo(@"Flir connect called (placeholder)");
46
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{ @"identity": identity ?: @{} }];
47
+ resolve(@(YES));
48
+ });
49
+ }
50
+
51
+ RCT_EXPORT_METHOD(disconnect)
52
+ {
53
+ dispatch_async(dispatch_get_main_queue(), ^{
54
+ RCTLogInfo(@"Flir disconnect called (placeholder)");
55
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{}];
56
+ });
57
+ }
58
+
59
+ RCT_EXPORT_METHOD(getTemperatureAt:(nonnull NSNumber *)x y:(nonnull NSNumber *)y resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
60
+ // Return the last sampled temperature from the preview/state singleton
61
+ dispatch_async(dispatch_get_main_queue(), ^{
62
+ double t = [FlirState shared].lastTemperature;
63
+ if (isnan(t)) {
64
+ resolve([NSNull null]);
65
+ } else {
66
+ resolve(@(t));
67
+ }
68
+ });
69
+ }
70
+
71
+ RCT_EXPORT_METHOD(isEmulator:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
72
+ dispatch_async(dispatch_get_main_queue(), ^{
73
+ resolve(@(self.isEmulatorMode));
74
+ });
75
+ }
76
+
77
+ RCT_EXPORT_METHOD(isDeviceConnected:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
78
+ dispatch_async(dispatch_get_main_queue(), ^{
79
+ resolve(@(self.isPhysicalDeviceConnected));
80
+ });
81
+ }
82
+
83
+ RCT_EXPORT_METHOD(getConnectedDeviceInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
84
+ dispatch_async(dispatch_get_main_queue(), ^{
85
+ if (self.connectedIdentity == nil) {
86
+ resolve(@"Not connected");
87
+ } else if (self.isEmulatorMode) {
88
+ resolve([NSString stringWithFormat:@"Emulator (%@)", [self.connectedIdentity deviceId]]);
89
+ } else {
90
+ resolve([NSString stringWithFormat:@"Physical device (%@)", [self.connectedIdentity deviceId]]);
91
+ }
92
+ });
93
+ }
94
+
95
+ RCT_EXPORT_METHOD(startEmulatorMode:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
96
+ dispatch_async(dispatch_get_main_queue(), ^{
97
+ [self initializeEmulatorMode];
98
+ resolve(@(YES));
99
+ });
100
+ }
101
+
102
+ - (void)initializeEmulatorMode
103
+ {
104
+ if (!self.discovery) {
105
+ self.discovery = [[FLIRDiscovery alloc] init];
106
+ self.discovery.delegate = self;
107
+ }
108
+
109
+ // Create emulator identity for testing; use FLIRCameraType for compatibility
110
+ FLIRIdentity *emulatorIdentity = [[FLIRIdentity alloc] initWithEmulatorType:FLIRCameraType_flirOne];
111
+ if (emulatorIdentity) {
112
+ self.connectedIdentity = emulatorIdentity;
113
+ self.isEmulatorMode = YES;
114
+ self.isPhysicalDeviceConnected = NO;
115
+
116
+ // Start discovery for emulator
117
+ [self.discovery start:FLIRCommunicationInterfaceEmulator cameraType:FLIRCameraType_flirOne];
118
+
119
+ // Emit connection event
120
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{
121
+ @"identity": @{
122
+ @"deviceId": [emulatorIdentity deviceId] ?: @"Emulator",
123
+ @"isEmulator": @(YES)
124
+ },
125
+ @"deviceType": @"emulator",
126
+ @"isEmulator": @(YES)
127
+ }];
128
+ }
129
+ }
130
+
131
+ #pragma mark - FLIRDiscoveryEventDelegate
132
+
133
+ - (void)cameraDiscovered:(FLIRDiscoveredCamera *)discoveredCamera {
134
+ FLIRIdentity *identity = discoveredCamera.identity;
135
+
136
+ BOOL isRealDevice = ([identity communicationInterface] != FLIRCommunicationInterfaceEmulator);
137
+
138
+ if (isRealDevice && !self.isPhysicalDeviceConnected) {
139
+ self.connectedIdentity = identity;
140
+ self.isEmulatorMode = NO;
141
+ self.isPhysicalDeviceConnected = YES;
142
+ [self connectToDevice:identity];
143
+ } else if (!isRealDevice && !self.isPhysicalDeviceConnected) {
144
+ self.connectedIdentity = identity;
145
+ self.isEmulatorMode = YES;
146
+ self.isPhysicalDeviceConnected = NO;
147
+ [self connectToDevice:identity];
148
+ }
149
+ }
150
+
151
+ #pragma mark - Helper Methods
152
+
153
+ - (void)connectToDevice:(FLIRIdentity *)identity {
154
+ if (!self.camera) {
155
+ self.camera = [[FLIRCamera alloc] init];
156
+ }
157
+
158
+ NSError *error;
159
+ BOOL connected = [self.camera connect:identity error:&error];
160
+
161
+ if (connected) {
162
+ NSString *deviceType = self.isEmulatorMode ? @"emulator" : @"device";
163
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{
164
+ @"identity": @{
165
+ @"deviceId": [identity deviceId] ?: @"Unknown",
166
+ @"isEmulator": @(self.isEmulatorMode)
167
+ },
168
+ @"deviceType": deviceType,
169
+ @"isEmulator": @(self.isEmulatorMode)
170
+ }];
171
+ } else {
172
+ RCTLogError(@"Failed to connect to FLIR device: %@", error.localizedDescription);
173
+ }
174
+ }
175
+
176
+ #pragma mark - FLIRDiscoveryEventDelegate
177
+
178
+ - (void)cameraFound:(FLIRIdentity *)identity
179
+ {
180
+ self.connectedIdentity = identity;
181
+ self.isEmulatorMode = ([identity communicationInterface] == FLIRCommunicationInterfaceEmulator);
182
+ self.isPhysicalDeviceConnected = !self.isEmulatorMode;
183
+
184
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{
185
+ @"identity": @{
186
+ @"deviceId": [identity deviceId] ?: @"Unknown",
187
+ @"isEmulator": @(self.isEmulatorMode)
188
+ },
189
+ @"deviceType": self.isEmulatorMode ? @"emulator" : @"physical",
190
+ @"isEmulator": @(self.isEmulatorMode)
191
+ }];
192
+ }
193
+
194
+ - (void)cameraLost:(FLIRIdentity *)identity
195
+ {
196
+ self.connectedIdentity = nil;
197
+ self.isEmulatorMode = NO;
198
+ self.isPhysicalDeviceConnected = NO;
199
+
200
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{
201
+ @"identity": @{
202
+ @"deviceId": [identity deviceId] ?: @"Unknown",
203
+ @"isEmulator": @([identity communicationInterface] == FLIRCommunicationInterfaceEmulator)
204
+ },
205
+ @"wasEmulator": @([identity communicationInterface] == FLIRCommunicationInterfaceEmulator)
206
+ }];
207
+ }
208
+
209
+ - (void)discoveryError:(NSString *)error netServiceError:(int)nsnetserviceserror on:(FLIRCommunicationInterface)iface
210
+ {
211
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirError" body:@{
212
+ @"error": error ?: @"Unknown discovery error",
213
+ @"type": @"discovery",
214
+ @"interface": @(iface)
215
+ }];
216
+ }
217
+
218
+ @end
219
+ #import "FlirModule.h"
220
+ #import "FlirEventEmitter.h"
221
+ #import "FlirState.h"
222
+ #import <ThermalSDK/ThermalSDK.h>
223
+ #ifndef F1_gen3
224
+ #define F1_gen3 FLIRCameraType_flirOne
225
+ #endif
226
+ #import <React/RCTLog.h>
227
+
228
+
229
+ @interface FlirModule()
230
+ <FLIRDiscoveryEventDelegate>
231
+ @property (nonatomic, strong) FLIRDiscovery *discovery;
232
+ @property (nonatomic, strong) FLIRCamera *camera;
233
+ @property (nonatomic, strong) FLIRIdentity *connectedIdentity;
234
+ @property (nonatomic, assign) BOOL isEmulatorMode;
235
+ @property (nonatomic, assign) BOOL isPhysicalDeviceConnected;
236
+ @end
237
+
238
+ @implementation FlirModule
239
+
240
+ RCT_EXPORT_MODULE(FlirIOS);
241
+
242
+ RCT_EXPORT_METHOD(startDiscovery)
243
+ {
244
+ // Hook into ThermalSDK discovery when ready. For now, emit an event to JS.
245
+ dispatch_async(dispatch_get_main_queue(), ^{
246
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{ @"msg": @"discovery-started" }];
247
+ RCTLogInfo(@"FLIR discovery started (placeholder)");
248
+ });
249
+ }
250
+
251
+ RCT_EXPORT_METHOD(stopDiscovery)
252
+ {
253
+ dispatch_async(dispatch_get_main_queue(), ^{
254
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{ @"msg": @"discovery-stopped" }];
255
+ RCTLogInfo(@"FLIR discovery stopped (placeholder)");
256
+ });
257
+ }
258
+
259
+ RCT_EXPORT_METHOD(connect:(NSDictionary *)identity resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
260
+ {
261
+ // TODO: implement real connect using ThermalSDK and identity info
262
+ dispatch_async(dispatch_get_main_queue(), ^{
263
+ RCTLogInfo(@"Flir connect called (placeholder)");
264
+ // emit a connected event
265
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{ @"identity": identity ?: @{} }];
266
+ resolve(@(YES));
267
+ });
268
+ }
269
+
270
+ RCT_EXPORT_METHOD(disconnect)
271
+ {
272
+ dispatch_async(dispatch_get_main_queue(), ^{
273
+ RCTLogInfo(@"Flir disconnect called (placeholder)");
274
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{}];
275
+ });
276
+ }
277
+
278
+ RCT_EXPORT_METHOD(getTemperatureAt:(nonnull NSNumber *)x y:(nonnull NSNumber *)y resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)\n{\n // Return the last sampled temperature from the preview/state singleton\n dispatch_async(dispatch_get_main_queue(), ^{\n double t = [FlirState shared].lastTemperature;\n if (isnan(t)) {\n resolve([NSNull null]);\n } else {\n resolve(@(t));\n }\n });\n}\n\nRCT_EXPORT_METHOD(isEmulator:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)\n{\n dispatch_async(dispatch_get_main_queue(), ^{\n resolve(@(self.isEmulatorMode));\n });\n}\n\nRCT_EXPORT_METHOD(isDeviceConnected:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)\n{\n dispatch_async(dispatch_get_main_queue(), ^{\n resolve(@(self.isPhysicalDeviceConnected));\n });\n}\n\nRCT_EXPORT_METHOD(getConnectedDeviceInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)\n{\n dispatch_async(dispatch_get_main_queue(), ^{\n if (self.connectedIdentity == nil) {\n resolve(@\"Not connected\");\n } else if (self.isEmulatorMode) {\n resolve([NSString stringWithFormat:@\"Emulator (%@)\", self.connectedIdentity.deviceId]);\n } else {\n resolve([NSString stringWithFormat:@\"Physical device (%@)\", self.connectedIdentity.deviceId]);\n }\n });\n}\n\nRCT_EXPORT_METHOD(startEmulatorMode:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)\n{\n dispatch_async(dispatch_get_main_queue(), ^{\n [self initializeEmulatorMode];\n resolve(@(YES));\n });\n}\n\n- (void)initializeEmulatorMode\n{\n if (!self.discovery) {\n self.discovery = [[FLIRDiscovery alloc] init];\n self.discovery.delegate = self;\n }\n \n // Create emulator identity for testing\n FLIRIdentity *emulatorIdentity = [[FLIRIdentity alloc] initWithEmulatorType:F1_gen3];\n if (emulatorIdentity) {\n self.connectedIdentity = emulatorIdentity;\n self.isEmulatorMode = YES;\n self.isPhysicalDeviceConnected = NO;\n \n // Start discovery for emulator\n [self.discovery start:FLIRCommunicationInterfaceEmulator cameraType:F1_gen3];\n \n // Emit connection event\n [[FlirEventEmitter shared] sendDeviceEvent:@\"FlirDeviceConnected\" body:@{\n @\"identity\": @{\n @\"deviceId\": emulatorIdentity.deviceId ?: @\"Emulator\",\n @\"isEmulator\": @(YES)\n },\n @\"deviceType\": @\"emulator\",\n @\"isEmulator\": @(YES)\n }];\n }\n}
279
+
280
+ #pragma mark - FLIRDiscoveryEventDelegate
281
+
282
+ - (void)cameraDiscovered:(FLIRDiscoveredCamera *)discoveredCamera {
283
+ FLIRIdentity *identity = discoveredCamera.identity;
284
+
285
+ // Check if this is a real device or emulator
286
+ BOOL isRealDevice = (identity.communicationInterface != FLIRCommunicationInterfaceEmulator);
287
+
288
+ if (isRealDevice && !self.isPhysicalDeviceConnected) {
289
+ // Prioritize real device over emulator
290
+ self.connectedIdentity = identity;
291
+ self.isEmulatorMode = NO;
292
+ self.isPhysicalDeviceConnected = YES;
293
+
294
+ // Connect to real device
295
+ [self connectToDevice:identity];
296
+ } else if (!isRealDevice && !self.isPhysicalDeviceConnected) {
297
+ // Use emulator if no real device found
298
+ self.connectedIdentity = identity;
299
+ self.isEmulatorMode = YES;
300
+ self.isPhysicalDeviceConnected = NO;
301
+
302
+ // Connect to emulator
303
+ [self connectToDevice:identity];
304
+ }
305
+ }
306
+
307
+ #pragma mark - Helper Methods
308
+
309
+ - (void)connectToDevice:(FLIRIdentity *)identity {
310
+ if (!self.camera) {
311
+ self.camera = [[FLIRCamera alloc] init];
312
+ }
313
+
314
+ NSError *error;
315
+ BOOL connected = [self.camera connect:identity error:&error];
316
+
317
+ if (connected) {
318
+ NSString *deviceType = self.isEmulatorMode ? @"emulator" : @"device";
319
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{
320
+ @"identity": @{
321
+ @"deviceId": identity.deviceId ?: @"Unknown",
322
+ @"isEmulator": @(self.isEmulatorMode)
323
+ },
324
+ @"deviceType": deviceType,
325
+ @"isEmulator": @(self.isEmulatorMode)
326
+ }];
327
+ } else {
328
+ RCTLogError(@"Failed to connect to FLIR device: %@", error.localizedDescription);
329
+ }
330
+ }
331
+
332
+ #pragma mark - FLIRDiscoveryEventDelegate
333
+
334
+ - (void)cameraFound:(FLIRIdentity *)identity
335
+ {
336
+ // Store the found camera identity
337
+ self.connectedIdentity = identity;
338
+
339
+ // Check if this is an emulator
340
+ self.isEmulatorMode = (identity.communicationInterface == FLIRCommunicationInterfaceEmulator);
341
+ self.isPhysicalDeviceConnected = !self.isEmulatorMode;
342
+
343
+ // Emit connection event
344
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:@{
345
+ @"identity": @{
346
+ @"deviceId": identity.deviceId ?: @"Unknown",
347
+ @"isEmulator": @(self.isEmulatorMode)
348
+ },
349
+ @"deviceType": self.isEmulatorMode ? @"emulator" : @"physical",
350
+ @"isEmulator": @(self.isEmulatorMode)
351
+ }];
352
+ }
353
+
354
+ - (void)cameraLost:(FLIRIdentity *)identity
355
+ {
356
+ // Clear connection state
357
+ self.connectedIdentity = nil;
358
+ self.isEmulatorMode = NO;
359
+ self.isPhysicalDeviceConnected = NO;
360
+
361
+ // Emit disconnection event
362
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceDisconnected" body:@{
363
+ @"identity": @{
364
+ @"deviceId": identity.deviceId ?: @"Unknown",
365
+ @"isEmulator": @(identity.communicationInterface == FLIRCommunicationInterfaceEmulator)
366
+ },
367
+ @"wasEmulator": @(identity.communicationInterface == FLIRCommunicationInterfaceEmulator)
368
+ }];
369
+ }
370
+
371
+ - (void)discoveryError:(NSString *)error netServiceError:(int)nsnetserviceserror on:(FLIRCommunicationInterface)iface
372
+ {
373
+ // Emit error event
374
+ [[FlirEventEmitter shared] sendDeviceEvent:@"FlirError" body:@{
375
+ @"error": error ?: @"Unknown discovery error",
376
+ @"type": @"discovery",
377
+ @"interface": @(iface)
378
+ }];
379
+ }
380
+
381
+ @end
@@ -0,0 +1,13 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface FlirPreviewView : UIView
6
+
7
+ @property (nonatomic, assign) double lastTemperature;
8
+
9
+ - (void)updateWithFrameImage:(UIImage *)image temperature:(double)temperature;
10
+
11
+ @end
12
+
13
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,24 @@
1
+ #import "FlirPreviewView.h"
2
+ #import "FlirState.h"
3
+
4
+ @implementation FlirPreviewView
5
+
6
+ - (instancetype)initWithFrame:(CGRect)frame
7
+ {
8
+ if (self = [super initWithFrame:frame]) {
9
+ self.backgroundColor = [UIColor blackColor];
10
+ _lastTemperature = NAN;
11
+ }
12
+ return self;
13
+ }
14
+
15
+ - (void)updateWithFrameImage:(UIImage *)image temperature:(double)temperature
16
+ {
17
+ dispatch_async(dispatch_get_main_queue(), ^{
18
+ self.layer.contents = (id)image.CGImage;
19
+ self.lastTemperature = temperature;
20
+ [FlirState shared].lastTemperature = temperature;
21
+ });
22
+ }
23
+
24
+ @end
@@ -0,0 +1,20 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface FlirState : NSObject
6
+
7
+ @property (nonatomic, assign) double lastTemperature;
8
+ @property (nonatomic, copy, nullable) void (^onTemperatureUpdate)(double temperature, int x, int y);
9
+ @property (nonatomic, copy, nullable) void (^onTextureUpdate)(UIImage *_Nonnull image, int textureUnit);
10
+ @property (nonatomic, strong, nullable) UIImage *latestImage;
11
+
12
+ + (instancetype)shared;
13
+ - (double)getTemperatureAt:(int)x y:(int)y;
14
+ - (void)updateFrame:(UIImage *_Nonnull)image;
15
+ - (void)updateFrame:(UIImage *_Nonnull)image withTemperatureData:(NSArray<NSNumber *> *_Nullable)tempData;
16
+ - (double)queryTemperatureAtPoint:(int)x y:(int)y;
17
+
18
+ @end
19
+
20
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,79 @@
1
+ #import "FlirState.h"
2
+
3
+ static FlirState *_sharedState = nil;
4
+
5
+ @implementation FlirState {
6
+ NSArray<NSNumber *> *_temperatureData; // Flattened array of temperature values
7
+ int _imageWidth;
8
+ int _imageHeight;
9
+ }
10
+
11
+ + (instancetype)shared
12
+ {
13
+ static dispatch_once_t onceToken;
14
+ dispatch_once(&onceToken, ^{
15
+ _sharedState = [FlirState new];
16
+ _sharedState.lastTemperature = NAN;
17
+ _sharedState.latestImage = nil;
18
+ _sharedState->_temperatureData = nil;
19
+ _sharedState->_imageWidth = 0;
20
+ _sharedState->_imageHeight = 0;
21
+ });
22
+ return _sharedState;
23
+ }
24
+
25
+ - (double)getTemperatureAt:(int)x y:(int)y
26
+ {
27
+ // Return cached temperature (will be updated by thermal SDK callbacks)
28
+ // In production, this would query the actual thermal image data at (x,y)
29
+ return self.lastTemperature;
30
+ }
31
+
32
+ - (void)updateFrame:(UIImage *)image
33
+ {
34
+ [self updateFrame:image withTemperatureData:nil];
35
+ }
36
+
37
+ - (void)updateFrame:(UIImage *)image withTemperatureData:(NSArray<NSNumber *> *)tempData
38
+ {
39
+ self.latestImage = image;
40
+
41
+ if (tempData != nil) {
42
+ _temperatureData = tempData;
43
+ _imageWidth = (int)image.size.width;
44
+ _imageHeight = (int)image.size.height;
45
+ }
46
+
47
+ // Invoke texture callback for native Metal filters (texture unit 7)
48
+ if (self.onTextureUpdate) {
49
+ self.onTextureUpdate(image, 7);
50
+ }
51
+
52
+ // Sample temperature at center point and invoke callback
53
+ if (self.onTemperatureUpdate) {
54
+ double temp = [self getTemperatureAt:80 y:60];
55
+ self.onTemperatureUpdate(temp, 80, 60);
56
+ }
57
+ }
58
+
59
+ - (double)queryTemperatureAtPoint:(int)x y:(int)y
60
+ {
61
+ if (_temperatureData == nil || _imageWidth == 0 || _imageHeight == 0) {
62
+ return NAN;
63
+ }
64
+
65
+ // Bounds check
66
+ if (x < 0 || x >= _imageWidth || y < 0 || y >= _imageHeight) {
67
+ return NAN;
68
+ }
69
+
70
+ // Access flattened array: index = y * width + x
71
+ NSInteger index = y * _imageWidth + x;
72
+ if (index < 0 || index >= (NSInteger)[_temperatureData count]) {
73
+ return NAN;
74
+ }
75
+
76
+ return [_temperatureData[index] doubleValue];
77
+ }
78
+
79
+ @end
@@ -0,0 +1,9 @@
1
+ #import <React/RCTViewManager.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface FlirViewManager : RCTViewManager
6
+
7
+ @end
8
+
9
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,16 @@
1
+ #import "FlirViewManager.h"
2
+ #import "FlirPreviewView.h"
3
+ #import <React/RCTBridge.h>
4
+ #import <React/RCTUIManager.h>
5
+
6
+ @implementation FlirViewManager
7
+
8
+ RCT_EXPORT_MODULE(FlirView)
9
+
10
+ - (UIView *)view
11
+ {
12
+ FlirPreviewView *v = [[FlirPreviewView alloc] initWithFrame:CGRectZero];
13
+ return v;
14
+ }
15
+
16
+ @end
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "ilabs-flir",
3
+ "version": "1.0.0",
4
+ "description": "FLIR Thermal SDK for React Native - On-Demand Download",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "scripts": {
8
+ "download-sdk": "node scripts/download-sdk.js",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "files": [
12
+ "src/",
13
+ "android/Flir/src/",
14
+ "android/Flir/build.gradle.kts",
15
+ "android/Flir/libs/",
16
+ "ios/Flir/src/",
17
+ "ios/Flir/SDKLoader/",
18
+ "ios/Flir/libs/.gitkeep",
19
+ "ios/Flir/Framework/",
20
+ "app.plugin.js",
21
+ "Flir.podspec",
22
+ "sdk-manifest.json",
23
+ "scripts/",
24
+ "expo-module.config.json",
25
+ "react-native.config.js"
26
+ ],
27
+ "keywords": [
28
+ "flir",
29
+ "thermal",
30
+ "thermal-camera",
31
+ "react-native",
32
+ "expo",
33
+ "expo-plugin",
34
+ "config-plugin",
35
+ "flir-one",
36
+ "thermal-imaging",
37
+ "infrared"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/PraveenOjha/flir.git"
42
+ },
43
+ "homepage": "https://github.com/PraveenOjha/flir#readme",
44
+ "bugs": {
45
+ "url": "https://github.com/PraveenOjha/flir/issues"
46
+ },
47
+ "author": "Praveen Ojha",
48
+ "license": "MIT",
49
+ "peerDependencies": {
50
+ "react": "*",
51
+ "react-native": ">=0.60.0"
52
+ },
53
+ "dependencies": {
54
+ "@expo/config-plugins": "^7.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "typescript": "^5.0.0",
58
+ "@types/react": "*",
59
+ "@types/react-native": "*"
60
+ }
61
+ }
@@ -0,0 +1,14 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ android: {
5
+ sourceDir: './android/Flir',
6
+ packageImportPath: 'import flir.android.FlirPackage;',
7
+ packageInstance: 'new FlirPackage()',
8
+ },
9
+ ios: {
10
+ podspecPath: './Flir.podspec',
11
+ },
12
+ },
13
+ },
14
+ };
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # Script to copy necessary iOS frameworks into ios/Flir/libs
5
+ # by default ThermalSDK.framework is located under ios/Flir/
6
+
7
+ DEST_DIR="$(pwd)/ios/Flir/libs"
8
+ mkdir -p "$DEST_DIR"
9
+
10
+ # copy ThermalSDK.framework if it exists
11
+ SRC_THERMAL="$PWD/ios/Flir/ThermalSDK.framework"
12
+ if [ -d "$SRC_THERMAL" ]; then
13
+ echo "Copying ThermalSDK.framework -> $DEST_DIR"
14
+ rm -rf "$DEST_DIR/ThermalSDK.framework"
15
+ cp -R "$SRC_THERMAL" "$DEST_DIR/"
16
+ else
17
+ echo "Warning: $SRC_THERMAL not found. Please add ThermalSDK.framework to ios/Flir/libs manually or place it at ios/Flir/"
18
+ fi
19
+
20
+ # Optionally copy FFmpeg-ish frameworks if present in project root (paths in sample PBX):
21
+ for lib in libswscale.5.dylib.framework libavfilter.7.dylib.framework liblive666.dylib.framework libavdevice.58.dylib.framework libavcodec.58.dylib.framework libavformat.58.dylib.framework libswresample.3.dylib.framework libavutil.56.dylib.framework; do
22
+ SRC_LIB="$PWD/../$lib"
23
+ if [ -d "$SRC_LIB" ]; then
24
+ echo "Copying $lib -> $DEST_DIR"
25
+ rm -rf "$DEST_DIR/$lib"
26
+ cp -R "$SRC_LIB" "$DEST_DIR/"
27
+ else
28
+ echo "Note: $lib not found at $SRC_LIB. Add it to ios/Flir/libs if required."
29
+ fi
30
+ done
31
+
32
+ echo "Done copying frameworks. The podspec references `ios/Flir/libs` — run 'pod install' from ios after adding frameworks."