ilabs-flir 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/Flir/src/FlirModule.m +153 -22
- package/package.json +1 -1
|
@@ -156,6 +156,39 @@ RCT_EXPORT_METHOD(removeListeners:(NSInteger)count) {
|
|
|
156
156
|
|
|
157
157
|
#pragma mark - Discovery Methods
|
|
158
158
|
|
|
159
|
+
// Network discovery on iOS 14+ requires Local Network privacy keys.
|
|
160
|
+
// In USB/Bluetooth-only builds (or when the user denied permission), attempting
|
|
161
|
+
// Bonjour discovery can fail noisily or crash depending on SDK internals.
|
|
162
|
+
// We default to enabling network discovery only when the host app declares
|
|
163
|
+
// NSLocalNetworkUsageDescription, and allow an explicit override via
|
|
164
|
+
// setNetworkDiscoveryEnabled.
|
|
165
|
+
- (BOOL)shouldEnableNetworkDiscovery {
|
|
166
|
+
// Explicit override if app sets it.
|
|
167
|
+
NSString *key = @"ilabsFlir.networkDiscoveryEnabled";
|
|
168
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
169
|
+
if ([defaults objectForKey:key] != nil) {
|
|
170
|
+
return [defaults boolForKey:key];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Safe default: require Local Network usage description to be present.
|
|
174
|
+
id desc = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocalNetworkUsageDescription"];
|
|
175
|
+
if ([desc isKindOfClass:[NSString class]] && ((NSString *)desc).length > 0) {
|
|
176
|
+
return YES;
|
|
177
|
+
}
|
|
178
|
+
return NO;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
RCT_EXPORT_METHOD(setNetworkDiscoveryEnabled:(BOOL)enabled
|
|
182
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
183
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
184
|
+
#if FLIR_SDK_AVAILABLE
|
|
185
|
+
[[NSUserDefaults standardUserDefaults] setBool:enabled forKey:@"ilabsFlir.networkDiscoveryEnabled"];
|
|
186
|
+
resolve(@(YES));
|
|
187
|
+
#else
|
|
188
|
+
resolve(@(YES));
|
|
189
|
+
#endif
|
|
190
|
+
}
|
|
191
|
+
|
|
159
192
|
RCT_EXPORT_METHOD(startDiscovery:(RCTPromiseResolveBlock)resolve
|
|
160
193
|
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
161
194
|
#if FLIR_SDK_AVAILABLE
|
|
@@ -169,21 +202,70 @@ RCT_EXPORT_METHOD(startDiscovery:(RCTPromiseResolveBlock)resolve
|
|
|
169
202
|
self.isScanning = YES;
|
|
170
203
|
[self.discoveredDevices removeAllObjects];
|
|
171
204
|
[self.identityMap removeAllObjects];
|
|
205
|
+
|
|
206
|
+
// Always expose emulator options to JS/UI so the user can connect even when
|
|
207
|
+
// physical devices are not present.
|
|
208
|
+
NSDictionary *emuOne = @{
|
|
209
|
+
@"id": @"emu:FLIR_ONE",
|
|
210
|
+
@"name": @"FLIR One Emulator",
|
|
211
|
+
@"communicationType": @"EMULATOR",
|
|
212
|
+
@"isEmulator": @(YES)
|
|
213
|
+
};
|
|
214
|
+
NSDictionary *emuEdge = @{
|
|
215
|
+
@"id": @"emu:FLIR_ONE_EDGE",
|
|
216
|
+
@"name": @"FLIR One Edge Emulator",
|
|
217
|
+
@"communicationType": @"EMULATOR",
|
|
218
|
+
@"isEmulator": @(YES)
|
|
219
|
+
};
|
|
220
|
+
[self.discoveredDevices addObjectsFromArray:@[ emuOne, emuEdge ]];
|
|
221
|
+
|
|
222
|
+
NSDictionary *initialDevicesBody = @{
|
|
223
|
+
@"devices": self.discoveredDevices,
|
|
224
|
+
@"count": @(self.discoveredDevices.count)
|
|
225
|
+
};
|
|
226
|
+
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDevicesFound" body:initialDevicesBody];
|
|
172
227
|
|
|
173
228
|
if (!self.discovery) {
|
|
174
229
|
self.discovery = [[FLIRDiscovery alloc] init];
|
|
175
230
|
self.discovery.delegate = self;
|
|
176
231
|
}
|
|
177
232
|
|
|
178
|
-
// Start discovery on
|
|
233
|
+
// Start discovery on allowed interfaces.
|
|
234
|
+
// Always include wired/BLE/emulator. Only include network when the app has
|
|
235
|
+
// Local Network usage description (or the app explicitly enabled it).
|
|
179
236
|
FLIRCommunicationInterface interfaces = FLIRCommunicationInterfaceLightning |
|
|
180
|
-
FLIRCommunicationInterfaceNetwork |
|
|
181
237
|
FLIRCommunicationInterfaceFlirOneWireless |
|
|
182
|
-
FLIRCommunicationInterfaceEmulator
|
|
238
|
+
FLIRCommunicationInterfaceEmulator |
|
|
239
|
+
FLIRCommunicationInterfaceUSB;
|
|
240
|
+
if ([self shouldEnableNetworkDiscovery]) {
|
|
241
|
+
interfaces |= FLIRCommunicationInterfaceNetwork;
|
|
242
|
+
} else {
|
|
243
|
+
RCTLogInfo(@"[FlirModule] Network discovery disabled (missing NSLocalNetworkUsageDescription or overridden)");
|
|
244
|
+
}
|
|
183
245
|
[self.discovery start:interfaces];
|
|
184
246
|
|
|
185
247
|
[self emitStateChange:@"discovering"];
|
|
186
248
|
RCTLogInfo(@"[FlirModule] Discovery started");
|
|
249
|
+
|
|
250
|
+
__weak typeof(self) weakSelf = self;
|
|
251
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
252
|
+
__strong typeof(self) strongSelf = weakSelf;
|
|
253
|
+
if (!strongSelf) return;
|
|
254
|
+
if (!strongSelf.isScanning || strongSelf.isConnected) return;
|
|
255
|
+
|
|
256
|
+
BOOL hasRealDevice = NO;
|
|
257
|
+
for (NSDictionary *dev in strongSelf.discoveredDevices) {
|
|
258
|
+
NSString *did = dev[@"id"];
|
|
259
|
+
if (did.length > 0 && ![did hasPrefix:@"emu:"]) {
|
|
260
|
+
hasRealDevice = YES;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!hasRealDevice) {
|
|
265
|
+
[strongSelf emitStateChange:@"no_device_found"];
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
187
269
|
resolve(@(YES));
|
|
188
270
|
});
|
|
189
271
|
#else
|
|
@@ -219,6 +301,36 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
219
301
|
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
220
302
|
#if FLIR_SDK_AVAILABLE
|
|
221
303
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
304
|
+
// Synthetic emulator ids exposed during discovery.
|
|
305
|
+
if ([deviceId hasPrefix:@"emu:"]) {
|
|
306
|
+
NSString *typePart = [deviceId substringFromIndex:4];
|
|
307
|
+
FLIRCameraType cameraType = FLIRCameraType_flirOne;
|
|
308
|
+
if ([typePart.lowercaseString containsString:@"edge"]) {
|
|
309
|
+
cameraType = FLIRCameraType_flirOneEdge;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
FLIRIdentity *emulatorIdentity = [[FLIRIdentity alloc] initWithEmulatorType:cameraType];
|
|
313
|
+
if (!emulatorIdentity) {
|
|
314
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
315
|
+
reject(@"ERR_EMULATOR_INIT", @"Failed to create emulator identity", nil);
|
|
316
|
+
});
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
self.identityMap[[emulatorIdentity deviceId]] = emulatorIdentity;
|
|
321
|
+
|
|
322
|
+
[self performConnectionWithIdentity:emulatorIdentity completion:^(BOOL success, NSError *error) {
|
|
323
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
324
|
+
if (success) {
|
|
325
|
+
resolve(@(YES));
|
|
326
|
+
} else {
|
|
327
|
+
reject(@"ERR_CONNECTION_FAILED", error.localizedDescription ?: @"Connection failed", error);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}];
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
222
334
|
FLIRIdentity *identity = self.identityMap[deviceId];
|
|
223
335
|
if (!identity) {
|
|
224
336
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -269,7 +381,15 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
269
381
|
if (connected) {
|
|
270
382
|
self.connectedIdentity = identity;
|
|
271
383
|
self.connectedDeviceId = [identity deviceId];
|
|
272
|
-
|
|
384
|
+
NSString *displayName = [identity deviceId];
|
|
385
|
+
if ([identity communicationInterface] == FLIRCommunicationInterfaceEmulator) {
|
|
386
|
+
if ([identity cameraType] == FLIRCameraType_flirOneEdge || [identity cameraType] == FLIRCameraType_flirOneEdgePro) {
|
|
387
|
+
displayName = @"FLIR One Edge Emulator";
|
|
388
|
+
} else {
|
|
389
|
+
displayName = @"FLIR One Emulator";
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
self.connectedDeviceName = displayName;
|
|
273
393
|
self.isConnected = YES;
|
|
274
394
|
|
|
275
395
|
RCTLogInfo(@"[FlirModule] Connected to: %@", [identity deviceId]);
|
|
@@ -284,7 +404,6 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
284
404
|
|
|
285
405
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
286
406
|
[self emitDeviceConnected];
|
|
287
|
-
[self emitStateChange:@"connected"];
|
|
288
407
|
});
|
|
289
408
|
|
|
290
409
|
if (completion) completion(YES, nil);
|
|
@@ -613,23 +732,20 @@ RCT_EXPORT_METHOD(isPreferSdkRotation:(RCTPromiseResolveBlock)resolve
|
|
|
613
732
|
#pragma mark - Helper Methods
|
|
614
733
|
|
|
615
734
|
- (void)emitDeviceConnected {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
NSDictionary *body = @{
|
|
619
|
-
@"identity": @{
|
|
620
|
-
@"deviceId": self.connectedDeviceId ?: @"Unknown",
|
|
621
|
-
@"isEmulator": @(isEmu)
|
|
622
|
-
},
|
|
623
|
-
@"deviceType": isEmu ? @"emulator" : @"device",
|
|
624
|
-
@"isEmulator": @(isEmu),
|
|
625
|
-
@"state": @"connected"
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:body];
|
|
735
|
+
[self emitStateChange:@"connected"];
|
|
629
736
|
}
|
|
630
737
|
|
|
631
738
|
- (void)emitStateChange:(NSString *)state {
|
|
632
|
-
BOOL isEmu =
|
|
739
|
+
BOOL isEmu = NO;
|
|
740
|
+
#if FLIR_SDK_AVAILABLE
|
|
741
|
+
if (self.connectedIdentity) {
|
|
742
|
+
isEmu = ([self.connectedIdentity communicationInterface] == FLIRCommunicationInterfaceEmulator);
|
|
743
|
+
} else {
|
|
744
|
+
isEmu = [self.connectedDeviceName.lowercaseString containsString:@"emulator"];
|
|
745
|
+
}
|
|
746
|
+
#else
|
|
747
|
+
isEmu = [self.connectedDeviceName.lowercaseString containsString:@"emulator"];
|
|
748
|
+
#endif
|
|
633
749
|
|
|
634
750
|
NSDictionary *body = @{
|
|
635
751
|
@"state": state,
|
|
@@ -637,9 +753,17 @@ RCT_EXPORT_METHOD(isPreferSdkRotation:(RCTPromiseResolveBlock)resolve
|
|
|
637
753
|
@"isStreaming": @(self.isStreaming),
|
|
638
754
|
@"isEmulator": @(isEmu),
|
|
639
755
|
@"deviceName": self.connectedDeviceName ?: @"",
|
|
640
|
-
@"deviceId": self.connectedDeviceId ?: @""
|
|
756
|
+
@"deviceId": self.connectedDeviceId ?: @"",
|
|
757
|
+
@"identity": @{
|
|
758
|
+
@"deviceId": self.connectedDeviceId ?: @"",
|
|
759
|
+
@"isEmulator": @(isEmu)
|
|
760
|
+
}
|
|
641
761
|
};
|
|
642
|
-
|
|
762
|
+
|
|
763
|
+
// App JS listens for FlirDeviceConnected state transitions.
|
|
764
|
+
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirDeviceConnected" body:body];
|
|
765
|
+
|
|
766
|
+
// Keep legacy event for backwards compatibility.
|
|
643
767
|
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirStateChanged" body:body];
|
|
644
768
|
}
|
|
645
769
|
|
|
@@ -737,8 +861,15 @@ RCT_EXPORT_METHOD(isPreferSdkRotation:(RCTPromiseResolveBlock)resolve
|
|
|
737
861
|
}
|
|
738
862
|
|
|
739
863
|
- (void)discoveryError:(NSString *)error netServiceError:(int)nsnetserviceserror on:(FLIRCommunicationInterface)iface {
|
|
864
|
+
// Network discovery failures are expected when Local Network permission is missing/denied.
|
|
865
|
+
// Do not surface those as fatal errors; keep USB/BLE discovery running.
|
|
866
|
+
if ((iface & FLIRCommunicationInterfaceNetwork) == FLIRCommunicationInterfaceNetwork) {
|
|
867
|
+
RCTLogInfo(@"[FlirModule] Network discovery error (suppressed): %@ (%d)", error, nsnetserviceserror);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
740
871
|
RCTLogError(@"[FlirModule] Discovery error: %@ (%d)", error, nsnetserviceserror);
|
|
741
|
-
|
|
872
|
+
|
|
742
873
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
743
874
|
[[FlirEventEmitter shared] sendDeviceEvent:@"FlirError" body:@{
|
|
744
875
|
@"error": error ?: @"Unknown discovery error",
|