@mentra/bluetooth-sdk 3.2.1-dev.314 → 3.3.0-beta.942
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/README.md +72 -10
- package/android/build.gradle +15 -0
- package/android/lc3Lib/build.gradle +6 -1
- package/android/src/debug/java/com/mentra/bluetoothsdk/sgcs/NimoDiagnostics.kt +368 -0
- package/android/src/debug/java/com/mentra/bluetoothsdk/sgcs/NimoFramebufferProtocol.kt +156 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +63 -14
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalyticsTracker.kt +55 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +4 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkInstallId.kt +23 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +8 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkUpdateCheck.kt +184 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkUsageTracker.kt +388 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +133 -26
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +37 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +3 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +6 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +227 -36
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +24 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/NetworkDiagnostics.kt +25 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/VersionInfoResponseAccumulator.kt +13 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +3 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +33 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/G2ConnectionRecovery.kt +47 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTracker.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioServiceDiscovery.kt +70 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1TextSanitizer.kt +11 -32
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +72 -21
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/LatinTextSanitizer.kt +39 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +194 -40
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +12 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +314 -395
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCodec.kt +247 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCoordinator.kt +238 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasEncoder.kt +58 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasSession.kt +180 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SelectedDeviceAddress.kt +17 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SerializedGattCallback.kt +5 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/G2Text.kt +7 -0
- package/android/src/release/java/com/mentra/bluetoothsdk/sgcs/NimoDiagnostics.kt +20 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkAnalyticsTrackerTest.kt +58 -3
- package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkUpdateCheckTest.kt +124 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkUsageTrackerTest.kt +263 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/DashboardContentFormatterTest.kt +2 -2
- package/android/src/test/java/com/mentra/bluetoothsdk/DeviceIdentityPairingTest.kt +59 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/DeviceManagerSceneHandoffTest.kt +666 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/G2TextTest.kt +13 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/NetworkDiagnosticsTest.kt +55 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/ScanCompletionTest.kt +5 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/VersionInfoResponseAccumulatorTest.kt +71 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/debug/BleTraceLoggerTest.kt +69 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/services/G2ConnectionRecoveryTest.kt +51 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioServiceDiscoveryTest.kt +124 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G1PairingTest.kt +105 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +29 -4
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G2DisplayThreadTest.kt +120 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G2PairingTargetTest.kt +90 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveClassicDisconnectTest.kt +133 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveFileTransferSessionTest.kt +55 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveGattCallbackTest.kt +33 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveIncidentDispatchTest.kt +126 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingTargetTest.kt +65 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveWifiCredentialLogTest.kt +55 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraNexPairingTest.kt +151 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoBitmapAcceptanceTest.kt +105 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCodecTest.kt +233 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasCoordinatorTest.kt +348 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoCanvasSessionTest.kt +339 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoMicReconnectTest.kt +93 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +11 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/SelectedDeviceAddressTest.kt +27 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoDiagnosticsTest.kt +287 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoFramebufferProtocolTest.kt +127 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoLifecycleTest.kt +288 -0
- package/android/src/testDebug/java/com/mentra/bluetoothsdk/sgcs/NimoReadinessDriverTest.kt +205 -0
- package/build/BluetoothSdk.types.d.ts +16 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +1 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +4 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +5 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/MentraLocalNetworkModule.d.ts +23 -0
- package/build/_private/MentraLocalNetworkModule.d.ts.map +1 -1
- package/build/_private/MentraLocalNetworkModule.js.map +1 -1
- package/build/_private/nativeLogConsole.d.ts +19 -0
- package/build/_private/nativeLogConsole.d.ts.map +1 -1
- package/build/_private/nativeLogConsole.js +12 -0
- package/build/_private/nativeLogConsole.js.map +1 -1
- package/build/generated/changelogCatalog.d.ts +5 -2
- package/build/generated/changelogCatalog.d.ts.map +1 -1
- package/build/generated/changelogCatalog.js +6 -2
- package/build/generated/changelogCatalog.js.map +1 -1
- package/build/generated/releaseMetadata.js +6 -6
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/ota-transport/index.d.ts +4 -0
- package/build/ota-transport/index.d.ts.map +1 -1
- package/build/ota-transport/index.js +2 -0
- package/build/ota-transport/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +17 -4
- package/ios/MentraBluetoothSDK.podspec +3 -2
- package/ios/Source/BluetoothBackgroundMode.swift +32 -0
- package/ios/Source/BluetoothSdkDefaults.swift +7 -2
- package/ios/Source/Bridge.swift +15 -2
- package/ios/Source/DeviceManager.swift +102 -66
- package/ios/Source/DeviceStore.swift +26 -7
- package/ios/Source/GeneratedChangelogCatalog.swift +3 -2
- package/ios/Source/GeneratedReleaseMetadata.swift +6 -6
- package/ios/Source/MentraBluetoothSDK.swift +313 -54
- package/ios/Source/audio/GlassesMicWatchdog.swift +33 -0
- package/ios/Source/internal/BluetoothAvailability.swift +6 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +87 -13
- package/ios/Source/internal/BluetoothSdkAnalyticsTracker.swift +54 -1
- package/ios/Source/internal/BluetoothSdkInstallId.swift +21 -0
- package/ios/Source/internal/BluetoothSdkUpdateCheck.swift +223 -0
- package/ios/Source/internal/BluetoothSdkUsageTracker.swift +428 -0
- package/ios/Source/requests/DisplayRequests.swift +1 -1
- package/ios/Source/requests/VersionInfoResponseAccumulator.swift +11 -1
- package/ios/Source/sgcs/Ar99.swift +1 -2
- package/ios/Source/sgcs/G1.swift +68 -38
- package/ios/Source/sgcs/G1ConnectionTarget.swift +13 -0
- package/ios/Source/sgcs/G2.swift +261 -247
- package/ios/Source/sgcs/G2ConnectionSession.swift +101 -0
- package/ios/Source/sgcs/Mach1.swift +19 -7
- package/ios/Source/sgcs/MentraLive.swift +272 -197
- package/ios/Source/sgcs/MentraLiveFileTransferSession.swift +137 -0
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +47 -0
- package/ios/Source/sgcs/MentraNex.swift +8 -31
- package/ios/Source/sgcs/NexConnectionTarget.swift +15 -0
- package/ios/Source/sgcs/Nimo.swift +280 -408
- package/ios/Source/sgcs/NimoCanvasCodec.swift +272 -0
- package/ios/Source/sgcs/NimoCanvasCoordinator.swift +233 -0
- package/ios/Source/sgcs/NimoCanvasImage.swift +53 -0
- package/ios/Source/sgcs/NimoCanvasSession.swift +166 -0
- package/ios/Source/sgcs/NimoDiscovery.swift +49 -0
- package/ios/Source/sgcs/SGCManager.swift +25 -1
- package/ios/Source/sgcs/VuzixConnectionTarget.swift +15 -0
- package/ios/Source/status/DeviceStatus.swift +1 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +49 -189
- package/ios/Source/utils/AudioSessionMonitor.swift +44 -8
- package/ios/Source/utils/DemandDrivenAudioWorker.swift +150 -0
- package/ios/Source/utils/G1Text.swift +2 -24
- package/ios/Source/utils/G1TextSanitizer.swift +27 -0
- package/ios/Source/utils/G2Text.swift +11 -0
- package/ios/Source/utils/LatinTextSanitizer.swift +48 -0
- package/ios/Tests/BluetoothAudioRouteTests.swift +26 -0
- package/ios/Tests/BluetoothBackgroundModeTests.swift +32 -0
- package/ios/Tests/BluetoothSdkAnalyticsTrackerTests.swift +54 -3
- package/ios/Tests/BluetoothSdkUpdateCheckTests.swift +153 -0
- package/ios/Tests/BluetoothSdkUsageTrackerTests.swift +296 -0
- package/ios/Tests/DashboardContentFormatterTests.swift +2 -2
- package/ios/Tests/DashboardRenderingTests.swift +31 -0
- package/ios/Tests/DemandDrivenAudioWorkerTests.swift +157 -0
- package/ios/Tests/DeviceIdentityPairingTests.swift +49 -0
- package/ios/Tests/DeviceManagerReconnectTests.swift +362 -0
- package/ios/Tests/G1PairingTests.swift +162 -0
- package/ios/Tests/G1TextSanitizerTests.swift +32 -0
- package/ios/Tests/G2ConnectionSessionTests.swift +172 -0
- package/ios/Tests/G2TextTests.swift +10 -0
- package/ios/Tests/IncidentLogDispatchTests.swift +111 -0
- package/ios/Tests/MentraLiveFileTransferSessionTests.swift +47 -0
- package/ios/Tests/MentraLiveReconnectPolicyTests.swift +58 -0
- package/ios/Tests/MentraLiveSessionReadinessTests.swift +163 -0
- package/ios/Tests/NimoCanvasCodecTests.swift +217 -0
- package/ios/Tests/NimoCanvasSessionTests.swift +181 -0
- package/ios/Tests/NimoCanvasTransportTests.swift +186 -0
- package/ios/Tests/NimoDeviceCommandsTests.swift +16 -0
- package/ios/Tests/NimoDiscoveryTests.swift +79 -0
- package/ios/Tests/SGCConnectionTargetTests.swift +48 -0
- package/ios/Tests/VersionInfoCommandTests.swift +194 -0
- package/ios/Tests/VersionInfoResponseAccumulatorTests.swift +65 -0
- package/ios/Tests/VideoRecordingCommandTests.swift +81 -0
- package/ios/Tests/WifiCredentialLoggingTests.swift +42 -0
- package/package.json +2 -2
- package/plugin/build/withIos.d.ts +2 -0
- package/plugin/build/withIos.js +12 -1
- package/src/BluetoothSdk.types.ts +15 -0
- package/src/_private/BluetoothSdkModule.ts +10 -1
- package/src/_private/MentraLocalNetworkModule.ts +28 -0
- package/src/_private/nativeLogConsole.ts +19 -0
- package/src/generated/changelogCatalog.ts +6 -2
- package/src/generated/releaseMetadata.ts +6 -6
- package/src/index.ts +1 -0
- package/src/ota-transport/index.ts +10 -1
package/ios/Source/sgcs/G1.swift
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import Combine
|
|
9
9
|
import CoreBluetooth
|
|
10
|
-
import Foundation
|
|
11
10
|
import CoreGraphics
|
|
11
|
+
import Foundation
|
|
12
12
|
|
|
13
13
|
extension Data {
|
|
14
14
|
func chunked(into size: Int) -> [Data] {
|
|
@@ -232,7 +232,8 @@ actor ReconnectionManager {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
func start(onAttempt: @escaping @Sendable () async -> Bool) {
|
|
235
|
-
|
|
235
|
+
// Multiple command failures must not restart the delay between attempts.
|
|
236
|
+
guard !isRunning else { return }
|
|
236
237
|
attempts = 0
|
|
237
238
|
|
|
238
239
|
task = Task {
|
|
@@ -258,6 +259,9 @@ actor ReconnectionManager {
|
|
|
258
259
|
break
|
|
259
260
|
}
|
|
260
261
|
}
|
|
262
|
+
if !Task.isCancelled {
|
|
263
|
+
task = nil
|
|
264
|
+
}
|
|
261
265
|
}
|
|
262
266
|
}
|
|
263
267
|
|
|
@@ -312,7 +316,7 @@ class G1: NSObject, SGCManager {
|
|
|
312
316
|
|
|
313
317
|
func queryGalleryStatus() {}
|
|
314
318
|
|
|
315
|
-
func sendOtaStart(otaVersionUrl: String?) {}
|
|
319
|
+
func sendOtaStart(otaVersionUrl _: String?) {}
|
|
316
320
|
func sendOtaQueryStatus() {}
|
|
317
321
|
|
|
318
322
|
func ping() {}
|
|
@@ -450,13 +454,12 @@ class G1: NSObject, SGCManager {
|
|
|
450
454
|
}
|
|
451
455
|
|
|
452
456
|
private var centralManager: CBCentralManager?
|
|
457
|
+
private var scanRequested = false
|
|
453
458
|
|
|
454
459
|
private var leftPeripheral: CBPeripheral?
|
|
455
460
|
private var rightPeripheral: CBPeripheral?
|
|
456
461
|
private var connectedDevices: [String: (CBPeripheral?, CBPeripheral?)] = [:]
|
|
457
462
|
var lastConnectionTimestamp: Date = .distantPast
|
|
458
|
-
private var leftInitialized: Bool = false
|
|
459
|
-
private var rightInitialized: Bool = false
|
|
460
463
|
|
|
461
464
|
private var leftGlassUUID: UUID? {
|
|
462
465
|
get {
|
|
@@ -468,8 +471,10 @@ class G1: NSObject, SGCManager {
|
|
|
468
471
|
set {
|
|
469
472
|
if let newValue = newValue {
|
|
470
473
|
UserDefaults.standard.set(newValue.uuidString, forKey: "leftGlassUUID")
|
|
474
|
+
UserDefaults.standard.set(DEVICE_SEARCH_ID, forKey: "leftGlassSearchID")
|
|
471
475
|
} else {
|
|
472
476
|
UserDefaults.standard.removeObject(forKey: "leftGlassUUID")
|
|
477
|
+
UserDefaults.standard.removeObject(forKey: "leftGlassSearchID")
|
|
473
478
|
}
|
|
474
479
|
}
|
|
475
480
|
}
|
|
@@ -484,8 +489,10 @@ class G1: NSObject, SGCManager {
|
|
|
484
489
|
set {
|
|
485
490
|
if let newValue = newValue {
|
|
486
491
|
UserDefaults.standard.set(newValue.uuidString, forKey: "rightGlassUUID")
|
|
492
|
+
UserDefaults.standard.set(DEVICE_SEARCH_ID, forKey: "rightGlassSearchID")
|
|
487
493
|
} else {
|
|
488
494
|
UserDefaults.standard.removeObject(forKey: "rightGlassUUID")
|
|
495
|
+
UserDefaults.standard.removeObject(forKey: "rightGlassSearchID")
|
|
489
496
|
}
|
|
490
497
|
}
|
|
491
498
|
}
|
|
@@ -497,6 +504,8 @@ class G1: NSObject, SGCManager {
|
|
|
497
504
|
}
|
|
498
505
|
|
|
499
506
|
func forget() {
|
|
507
|
+
isDisconnecting = true
|
|
508
|
+
stopScan()
|
|
500
509
|
Task {
|
|
501
510
|
await heartbeatManager.stop()
|
|
502
511
|
await reconnectionManager.stop()
|
|
@@ -700,11 +709,13 @@ class G1: NSObject, SGCManager {
|
|
|
700
709
|
|
|
701
710
|
func connectById(_ id: String) {
|
|
702
711
|
DEVICE_SEARCH_ID = "_" + id + "_"
|
|
712
|
+
scanRequested = true
|
|
703
713
|
startScan()
|
|
704
714
|
}
|
|
705
715
|
|
|
706
716
|
func findCompatibleDevices() {
|
|
707
717
|
DEVICE_SEARCH_ID = "NOT_SET"
|
|
718
|
+
scanRequested = true
|
|
708
719
|
startScan()
|
|
709
720
|
}
|
|
710
721
|
|
|
@@ -732,8 +743,7 @@ class G1: NSObject, SGCManager {
|
|
|
732
743
|
)
|
|
733
744
|
stopScan()
|
|
734
745
|
|
|
735
|
-
//
|
|
736
|
-
getBatteryStatus()
|
|
746
|
+
// Battery requests start after both arms acknowledge initialization.
|
|
737
747
|
return true
|
|
738
748
|
}
|
|
739
749
|
|
|
@@ -981,11 +991,15 @@ class G1: NSObject, SGCManager {
|
|
|
981
991
|
connected = leftReady && rightReady
|
|
982
992
|
if fullyBooted {
|
|
983
993
|
stopReconnectionTimer()
|
|
994
|
+
if !prevLeftReady || !prevRightReady {
|
|
995
|
+
getBatteryStatus()
|
|
996
|
+
}
|
|
984
997
|
}
|
|
985
998
|
}
|
|
986
999
|
|
|
987
1000
|
func stopScan() {
|
|
988
|
-
|
|
1001
|
+
scanRequested = false
|
|
1002
|
+
centralManager?.stopScan()
|
|
989
1003
|
Bridge.log("G1: Stopped scanning for devices")
|
|
990
1004
|
}
|
|
991
1005
|
|
|
@@ -1007,6 +1021,7 @@ class G1: NSObject, SGCManager {
|
|
|
1007
1021
|
|
|
1008
1022
|
func disconnect() {
|
|
1009
1023
|
isDisconnecting = true
|
|
1024
|
+
stopScan()
|
|
1010
1025
|
leftGlassUUID = nil
|
|
1011
1026
|
rightGlassUUID = nil
|
|
1012
1027
|
stopReconnectionTimer()
|
|
@@ -1038,10 +1053,13 @@ class G1: NSObject, SGCManager {
|
|
|
1038
1053
|
let data = Data(chunks[0])
|
|
1039
1054
|
// CoreCommsService.log("SEND (\(side)) \(data.hexEncodedString())")
|
|
1040
1055
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1056
|
+
// A queued command can outlive the link that enqueued it. Missing
|
|
1057
|
+
// transport during discovery/pairing is not an ACK timeout and must
|
|
1058
|
+
// not start a reconnect loop before CoreBluetooth finishes connecting.
|
|
1059
|
+
let peripheral = side == "L" ? leftPeripheral : rightPeripheral
|
|
1060
|
+
guard !isDisconnecting, peripheral?.state == .connected,
|
|
1061
|
+
getWriteCharacteristic(for: peripheral) != nil
|
|
1062
|
+
else { return }
|
|
1045
1063
|
|
|
1046
1064
|
for i in 0 ..< chunks.count - 1 {
|
|
1047
1065
|
let chunk = chunks[i]
|
|
@@ -1502,18 +1520,9 @@ extension G1 {
|
|
|
1502
1520
|
/// don't call semaphore signals here as it's handled elswhere:
|
|
1503
1521
|
private func handleInitResponse(from peripheral: CBPeripheral, success: Bool) {
|
|
1504
1522
|
if peripheral == leftPeripheral {
|
|
1505
|
-
|
|
1506
|
-
// CoreCommsService.log("G1: Left arm initialized: \(success)")
|
|
1507
|
-
setReadiness(left: true, right: nil)
|
|
1523
|
+
setReadiness(left: success, right: nil)
|
|
1508
1524
|
} else if peripheral == rightPeripheral {
|
|
1509
|
-
|
|
1510
|
-
// CoreCommsService.log("G1: Right arm initialized: \(success)")
|
|
1511
|
-
setReadiness(left: nil, right: true)
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
// Only proceed if both glasses are initialized
|
|
1515
|
-
if leftInitialized, rightInitialized {
|
|
1516
|
-
setReadiness(left: true, right: true)
|
|
1525
|
+
setReadiness(left: nil, right: success)
|
|
1517
1526
|
}
|
|
1518
1527
|
}
|
|
1519
1528
|
|
|
@@ -1733,6 +1742,7 @@ extension G1 {
|
|
|
1733
1742
|
}
|
|
1734
1743
|
|
|
1735
1744
|
func getBatteryStatus() {
|
|
1745
|
+
guard !isDisconnecting, leftReady, rightReady else { return }
|
|
1736
1746
|
Bridge.log("G1: getBatteryStatus()")
|
|
1737
1747
|
let command: [UInt8] = [Commands.BLE_REQ_BATTERY.rawValue, 0x01]
|
|
1738
1748
|
queueChunks([command])
|
|
@@ -2364,6 +2374,7 @@ extension G1: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
|
2364
2374
|
|
|
2365
2375
|
private func startReconnectionTimer() {
|
|
2366
2376
|
Task {
|
|
2377
|
+
guard !isDisconnecting else { return }
|
|
2367
2378
|
await reconnectionManager.start { [weak self] in
|
|
2368
2379
|
guard let self else { return false }
|
|
2369
2380
|
|
|
@@ -2377,6 +2388,8 @@ extension G1: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
|
2377
2388
|
|
|
2378
2389
|
// Attempt to reconnect
|
|
2379
2390
|
await MainActor.run {
|
|
2391
|
+
guard !self.isDisconnecting else { return }
|
|
2392
|
+
self.scanRequested = true
|
|
2380
2393
|
self.startScan()
|
|
2381
2394
|
}
|
|
2382
2395
|
|
|
@@ -2401,17 +2414,22 @@ extension G1: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
|
2401
2414
|
}
|
|
2402
2415
|
|
|
2403
2416
|
Bridge.log("G1: 🔵 Attempting to connect by UUID")
|
|
2404
|
-
var
|
|
2417
|
+
var foundSides = 0
|
|
2405
2418
|
|
|
2406
2419
|
if let leftUUID = leftGlassUUID {
|
|
2407
2420
|
Bridge.log("G1: 🔵 Found stored left glass UUID: \(leftUUID.uuidString)")
|
|
2408
2421
|
let leftDevices = centralManager!.retrievePeripherals(withIdentifiers: [leftUUID])
|
|
2409
2422
|
|
|
2410
|
-
if let leftDevice = leftDevices.first
|
|
2423
|
+
if let leftDevice = leftDevices.first,
|
|
2424
|
+
G1ConnectionTarget.matchesCachedPeripheral(
|
|
2425
|
+
name: leftDevice.name, searchID: DEVICE_SEARCH_ID,
|
|
2426
|
+
cachedSearchID: UserDefaults.standard.string(forKey: "leftGlassSearchID")
|
|
2427
|
+
)
|
|
2428
|
+
{
|
|
2411
2429
|
Bridge.log(
|
|
2412
2430
|
"G1: 🔵 Successfully retrieved left glass: \(leftDevice.name ?? "Unknown")"
|
|
2413
2431
|
)
|
|
2414
|
-
|
|
2432
|
+
foundSides += 1
|
|
2415
2433
|
leftPeripheral = leftDevice
|
|
2416
2434
|
leftDevice.delegate = self
|
|
2417
2435
|
centralManager!.connect(
|
|
@@ -2428,11 +2446,16 @@ extension G1: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
|
2428
2446
|
Bridge.log("G1: 🔵 Found stored right glass UUID: \(rightUUID.uuidString)")
|
|
2429
2447
|
let rightDevices = centralManager!.retrievePeripherals(withIdentifiers: [rightUUID])
|
|
2430
2448
|
|
|
2431
|
-
if let rightDevice = rightDevices.first
|
|
2449
|
+
if let rightDevice = rightDevices.first,
|
|
2450
|
+
G1ConnectionTarget.matchesCachedPeripheral(
|
|
2451
|
+
name: rightDevice.name, searchID: DEVICE_SEARCH_ID,
|
|
2452
|
+
cachedSearchID: UserDefaults.standard.string(forKey: "rightGlassSearchID")
|
|
2453
|
+
)
|
|
2454
|
+
{
|
|
2432
2455
|
Bridge.log(
|
|
2433
2456
|
"G1: 🔵 Successfully retrieved right glass: \(rightDevice.name ?? "Unknown")"
|
|
2434
2457
|
)
|
|
2435
|
-
|
|
2458
|
+
foundSides += 1
|
|
2436
2459
|
rightPeripheral = rightDevice
|
|
2437
2460
|
rightDevice.delegate = self
|
|
2438
2461
|
centralManager!.connect(
|
|
@@ -2445,7 +2468,8 @@ extension G1: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
|
2445
2468
|
}
|
|
2446
2469
|
}
|
|
2447
2470
|
|
|
2448
|
-
|
|
2471
|
+
// Continue scanning if only one matching cached half was available.
|
|
2472
|
+
return foundSides == 2
|
|
2449
2473
|
}
|
|
2450
2474
|
|
|
2451
2475
|
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices _: Error?) {
|
|
@@ -2502,18 +2526,24 @@ extension G1: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
|
2502
2526
|
/// called whenever bluetooth is initialized / turned on or off:
|
|
2503
2527
|
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
2504
2528
|
DispatchQueue.main.async { [weak self] in
|
|
2505
|
-
|
|
2529
|
+
self?.handleBluetoothState(central.state)
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2506
2532
|
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2533
|
+
func handleBluetoothState(_ state: CBManagerState) {
|
|
2534
|
+
if state == .poweredOn {
|
|
2535
|
+
Bridge.log("G1: Bluetooth was powered on")
|
|
2536
|
+
setReadiness(left: false, right: false)
|
|
2510
2537
|
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2538
|
+
// Discovery has no search ID, but still needs to resume after the
|
|
2539
|
+
// central manager's initial .unknown state. Preserve selected-pair
|
|
2540
|
+
// reconnects after a power cycle, unless intentionally disconnected.
|
|
2541
|
+
let hasConnectionTarget = DEVICE_SEARCH_ID != "NOT_SET" && !DEVICE_SEARCH_ID.isEmpty
|
|
2542
|
+
if !isDisconnecting, scanRequested || hasConnectionTarget {
|
|
2543
|
+
startScan()
|
|
2516
2544
|
}
|
|
2545
|
+
} else {
|
|
2546
|
+
Bridge.log("G1: Bluetooth was turned off.")
|
|
2517
2547
|
}
|
|
2518
2548
|
}
|
|
2519
2549
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
enum G1ConnectionTarget {
|
|
4
|
+
/// A legacy UUID needs a matching peripheral name; newly recorded UUIDs can
|
|
5
|
+
/// also reconnect without a name because their search ID was saved with them.
|
|
6
|
+
static func matchesCachedPeripheral(name: String?, searchID: String, cachedSearchID: String?) -> Bool {
|
|
7
|
+
guard searchID != "NOT_SET", searchID.count > 2 else { return false }
|
|
8
|
+
if let name, !name.isEmpty {
|
|
9
|
+
return name.contains("G1" + searchID)
|
|
10
|
+
}
|
|
11
|
+
return cachedSearchID == searchID
|
|
12
|
+
}
|
|
13
|
+
}
|