ilabs-flir 2.1.2 → 2.1.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.
- package/ios/Flir/src/FlirManager.swift +95 -51
- package/ios/Flir/src/FlirModule.m +44 -6
- package/package.json +1 -1
|
@@ -310,23 +310,57 @@ import ThermalSDK
|
|
|
310
310
|
discovery?.delegate = self
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
//
|
|
314
|
-
|
|
313
|
+
// Build interfaces based on available permissions
|
|
314
|
+
// Always include Lightning, USB, Wireless BLE, and Emulator
|
|
315
|
+
var interfaces: FLIRCommunicationInterface = [
|
|
315
316
|
.lightning,
|
|
316
|
-
.network,
|
|
317
317
|
.flirOneWireless,
|
|
318
318
|
.emulator
|
|
319
319
|
]
|
|
320
|
+
|
|
321
|
+
// Only add network discovery if NSLocalNetworkUsageDescription is present
|
|
322
|
+
// This prevents crashes/errors when user doesn't have iOS developer registration
|
|
323
|
+
// or hasn't declared network permission
|
|
324
|
+
if shouldEnableNetworkDiscovery() {
|
|
325
|
+
interfaces.insert(.network)
|
|
326
|
+
NSLog("[FlirManager] Network discovery enabled (NSLocalNetworkUsageDescription present)")
|
|
327
|
+
} else {
|
|
328
|
+
NSLog("[FlirManager] Network discovery disabled (no NSLocalNetworkUsageDescription)")
|
|
329
|
+
}
|
|
330
|
+
|
|
320
331
|
discovery?.start(interfaces)
|
|
321
332
|
|
|
322
333
|
emitStateChange("discovering")
|
|
323
|
-
NSLog("[FlirManager] Discovery started on interfaces: Lightning, Network, FlirOneWireless, Emulator")
|
|
334
|
+
NSLog("[FlirManager] Discovery started on interfaces: Lightning, \(interfaces.contains(.network) ? "Network, " : "")FlirOneWireless, Emulator")
|
|
324
335
|
#else
|
|
325
336
|
NSLog("[FlirManager] FLIR SDK not available - discovery disabled")
|
|
326
337
|
delegate?.onError("FLIR SDK not available")
|
|
327
338
|
#endif
|
|
328
339
|
}
|
|
329
340
|
|
|
341
|
+
/// Check if network discovery should be enabled based on Info.plist permission
|
|
342
|
+
private func shouldEnableNetworkDiscovery() -> Bool {
|
|
343
|
+
// Check for explicit override first
|
|
344
|
+
let key = "ilabsFlir.networkDiscoveryEnabled"
|
|
345
|
+
if UserDefaults.standard.object(forKey: key) != nil {
|
|
346
|
+
return UserDefaults.standard.bool(forKey: key)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Safe default: require Local Network usage description to be present
|
|
350
|
+
if let desc = Bundle.main.object(forInfoDictionaryKey: "NSLocalNetworkUsageDescription") as? String,
|
|
351
|
+
!desc.isEmpty {
|
|
352
|
+
return true
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return false
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/// Allow explicit override of network discovery (called from React Native)
|
|
359
|
+
@objc public func setNetworkDiscoveryEnabled(_ enabled: Bool) {
|
|
360
|
+
UserDefaults.standard.set(enabled, forKey: "ilabsFlir.networkDiscoveryEnabled")
|
|
361
|
+
NSLog("[FlirManager] Network discovery override set to: \(enabled)")
|
|
362
|
+
}
|
|
363
|
+
|
|
330
364
|
@objc public func stopDiscovery() {
|
|
331
365
|
NSLog("[FlirManager] Stopping discovery...")
|
|
332
366
|
|
|
@@ -366,64 +400,72 @@ import ThermalSDK
|
|
|
366
400
|
}
|
|
367
401
|
|
|
368
402
|
private func performConnection(identity: FLIRIdentity) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
403
|
+
// Use the proven connection pattern from FLIR SDK samples:
|
|
404
|
+
// FLIROneCameraSwift uses: pair(identity, code:) then connect()
|
|
405
|
+
|
|
406
|
+
if camera == nil {
|
|
407
|
+
camera = FLIRCamera()
|
|
408
|
+
camera?.delegate = self
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
guard let cam = camera else {
|
|
412
|
+
NSLog("[FlirManager] Failed to create FLIRCamera")
|
|
413
|
+
DispatchQueue.main.async { [weak self] in
|
|
414
|
+
self?.delegate?.onError("Failed to create camera instance")
|
|
373
415
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
416
|
+
return
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Handle authentication for generic cameras (network cameras)
|
|
420
|
+
if identity.cameraType() == .generic {
|
|
421
|
+
let certName = getCertificateName()
|
|
422
|
+
var status = FLIRAuthenticationStatus.pending
|
|
423
|
+
while status == .pending {
|
|
424
|
+
status = cam.authenticate(identity, trustedConnectionName: certName)
|
|
425
|
+
if status == .pending {
|
|
426
|
+
NSLog("[FlirManager] Waiting for camera authentication approval...")
|
|
427
|
+
Thread.sleep(forTimeInterval: 1.0)
|
|
385
428
|
}
|
|
386
429
|
}
|
|
430
|
+
NSLog("[FlirManager] Authentication status: \(status.rawValue)")
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
do {
|
|
434
|
+
// Step 1: Pair with identity (required for FLIR One devices)
|
|
435
|
+
// The code parameter is for BLE pairing, 0 for direct connection
|
|
436
|
+
try cam.pair(identity, code: 0)
|
|
437
|
+
NSLog("[FlirManager] Paired with: \(identity.deviceId())")
|
|
387
438
|
|
|
388
|
-
// Connect (
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if connectedOK {
|
|
404
|
-
connectedIdentity = identity
|
|
405
|
-
connectedDeviceId = identity.deviceId()
|
|
406
|
-
connectedDeviceName = identity.deviceId()
|
|
407
|
-
_isConnected = true
|
|
408
|
-
NSLog("[FlirManager] Connected to: \(identity.deviceId())")
|
|
409
|
-
} else {
|
|
410
|
-
NSLog("[FlirManager] Connection failed: no compatible connect API")
|
|
411
|
-
DispatchQueue.main.async { [weak self] in
|
|
412
|
-
self?.delegate?.onError("Connection failed: unsupported SDK API")
|
|
413
|
-
}
|
|
414
|
-
return
|
|
439
|
+
// Step 2: Connect (no identity parameter - uses paired identity)
|
|
440
|
+
try cam.connect()
|
|
441
|
+
NSLog("[FlirManager] Connected to: \(identity.deviceId())")
|
|
442
|
+
|
|
443
|
+
// Update state
|
|
444
|
+
connectedIdentity = identity
|
|
445
|
+
connectedDeviceId = identity.deviceId()
|
|
446
|
+
connectedDeviceName = identity.deviceId()
|
|
447
|
+
_isConnected = true
|
|
448
|
+
|
|
449
|
+
// Get camera info if available
|
|
450
|
+
if let remoteControl = cam.getRemoteControl(),
|
|
451
|
+
let cameraInfo = try? remoteControl.getCameraInformation() {
|
|
452
|
+
NSLog("[FlirManager] Camera info: \(cameraInfo)")
|
|
415
453
|
}
|
|
416
454
|
|
|
417
455
|
// Get streams
|
|
418
|
-
if let streams =
|
|
456
|
+
if let streams = cam.getStreams(), !streams.isEmpty {
|
|
419
457
|
NSLog("[FlirManager] Found \(streams.count) streams")
|
|
420
458
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
459
|
+
// Find and start the first thermal stream (preferred) or any stream
|
|
460
|
+
let thermalStream = streams.first { $0.isThermal } ?? streams.first
|
|
461
|
+
if let streamToStart = thermalStream {
|
|
462
|
+
startStreamInternal(streamToStart)
|
|
424
463
|
}
|
|
464
|
+
} else {
|
|
465
|
+
NSLog("[FlirManager] No streams available on camera")
|
|
425
466
|
}
|
|
426
467
|
|
|
468
|
+
// Notify delegate on main thread
|
|
427
469
|
DispatchQueue.main.async { [weak self] in
|
|
428
470
|
guard let self = self else { return }
|
|
429
471
|
let deviceInfo = FlirDeviceInfo(
|
|
@@ -437,7 +479,9 @@ import ThermalSDK
|
|
|
437
479
|
}
|
|
438
480
|
|
|
439
481
|
} catch {
|
|
440
|
-
NSLog("[FlirManager] Connection failed: \(error)")
|
|
482
|
+
NSLog("[FlirManager] Connection failed: \(error.localizedDescription)")
|
|
483
|
+
_isConnected = false
|
|
484
|
+
camera = nil
|
|
441
485
|
DispatchQueue.main.async { [weak self] in
|
|
442
486
|
self?.delegate?.onError("Connection failed: \(error.localizedDescription)")
|
|
443
487
|
}
|
|
@@ -363,7 +363,7 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
363
363
|
|
|
364
364
|
NSError *error = nil;
|
|
365
365
|
|
|
366
|
-
// Handle authentication for generic cameras
|
|
366
|
+
// Handle authentication for generic cameras (network cameras)
|
|
367
367
|
if ([identity cameraType] == FLIRCameraType_generic) {
|
|
368
368
|
NSString *certName = [self getCertificateName];
|
|
369
369
|
FLIRAuthenticationStatus status = pending;
|
|
@@ -374,9 +374,34 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
374
374
|
[NSThread sleepForTimeInterval:1.0];
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
+
RCTLogInfo(@"[FlirModule] Authentication status: %d", (int)status);
|
|
377
378
|
}
|
|
378
379
|
|
|
379
|
-
|
|
380
|
+
// Use the proven connection pattern from FLIR SDK samples:
|
|
381
|
+
// Step 1: Pair with identity (required for FLIR One devices)
|
|
382
|
+
@try {
|
|
383
|
+
[self.camera pair:identity code:0];
|
|
384
|
+
RCTLogInfo(@"[FlirModule] Paired with: %@", [identity deviceId]);
|
|
385
|
+
} @catch (NSException *exception) {
|
|
386
|
+
RCTLogError(@"[FlirModule] Pair failed: %@", exception.reason);
|
|
387
|
+
NSError *pairError = [NSError errorWithDomain:@"FlirModule" code:1001 userInfo:@{NSLocalizedDescriptionKey: exception.reason ?: @"Pair failed"}];
|
|
388
|
+
if (completion) completion(NO, pairError);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Step 2: Connect (no identity parameter - uses paired identity)
|
|
393
|
+
BOOL connected = NO;
|
|
394
|
+
@try {
|
|
395
|
+
[self.camera connect:&error];
|
|
396
|
+
connected = (error == nil);
|
|
397
|
+
if (connected) {
|
|
398
|
+
RCTLogInfo(@"[FlirModule] Connected to: %@", [identity deviceId]);
|
|
399
|
+
}
|
|
400
|
+
} @catch (NSException *exception) {
|
|
401
|
+
RCTLogError(@"[FlirModule] Connect exception: %@", exception.reason);
|
|
402
|
+
error = [NSError errorWithDomain:@"FlirModule" code:1002 userInfo:@{NSLocalizedDescriptionKey: exception.reason ?: @"Connect failed"}];
|
|
403
|
+
connected = NO;
|
|
404
|
+
}
|
|
380
405
|
|
|
381
406
|
if (connected) {
|
|
382
407
|
self.connectedIdentity = identity;
|
|
@@ -392,14 +417,25 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
392
417
|
self.connectedDeviceName = displayName;
|
|
393
418
|
self.isConnected = YES;
|
|
394
419
|
|
|
395
|
-
RCTLogInfo(@"[FlirModule]
|
|
420
|
+
RCTLogInfo(@"[FlirModule] Successfully connected to: %@", displayName);
|
|
396
421
|
|
|
397
|
-
// Get available streams
|
|
422
|
+
// Get available streams and prefer thermal stream
|
|
398
423
|
NSArray<FLIRStream *> *streams = [self.camera getStreams];
|
|
399
424
|
if (streams.count > 0) {
|
|
400
425
|
RCTLogInfo(@"[FlirModule] Found %lu streams", (unsigned long)streams.count);
|
|
401
|
-
|
|
402
|
-
|
|
426
|
+
|
|
427
|
+
// Find thermal stream (preferred) or use first stream
|
|
428
|
+
FLIRStream *streamToStart = nil;
|
|
429
|
+
for (FLIRStream *stream in streams) {
|
|
430
|
+
if (stream.isThermal) {
|
|
431
|
+
streamToStart = stream;
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (!streamToStart) {
|
|
436
|
+
streamToStart = streams[0];
|
|
437
|
+
}
|
|
438
|
+
[self startStreamInternal:streamToStart];
|
|
403
439
|
}
|
|
404
440
|
|
|
405
441
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -409,10 +445,12 @@ RCT_EXPORT_METHOD(connectToDevice:(NSString *)deviceId
|
|
|
409
445
|
if (completion) completion(YES, nil);
|
|
410
446
|
} else {
|
|
411
447
|
RCTLogError(@"[FlirModule] Connection failed: %@", error.localizedDescription);
|
|
448
|
+
self.camera = nil;
|
|
412
449
|
if (completion) completion(NO, error);
|
|
413
450
|
}
|
|
414
451
|
}
|
|
415
452
|
|
|
453
|
+
|
|
416
454
|
- (NSString *)getCertificateName {
|
|
417
455
|
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier] ?: @"com.flir.app";
|
|
418
456
|
NSString *key = [NSString stringWithFormat:@"%@-cert-name", bundleID];
|