@mentra/bluetooth-sdk 3.2.0-dev.191 โ†’ 3.2.0-dev.195

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 (80) hide show
  1. package/README.md +29 -0
  2. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +1 -3
  3. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
  4. package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +1 -1
  5. package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +1 -1
  6. package/android/src/main/java/com/mentra/bluetoothsdk/otaserver/MentraOtaServerModule.kt +1 -1
  7. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +1 -1
  8. package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1 -1
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Ar99.java +8 -8
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +1 -1
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +2 -2
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +1 -1
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +1 -1
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +2 -2
  16. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +2 -2
  17. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -1
  18. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +1 -1
  19. package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +1 -1
  20. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +7 -8
  21. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +2 -2
  22. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +34 -34
  23. package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +1 -1
  24. package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +4 -4
  25. package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +34 -34
  26. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +10 -10
  27. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +6 -6
  28. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NativeLog.java +89 -0
  29. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +2 -2
  30. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +10 -10
  31. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +7 -7
  32. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +16 -16
  33. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +26 -26
  34. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +1 -1
  35. package/android/src/test/java/com/mentra/bluetoothsdk/utils/NativeLogTest.kt +61 -0
  36. package/build/BluetoothSdk.types.d.ts +7 -0
  37. package/build/BluetoothSdk.types.d.ts.map +1 -1
  38. package/build/BluetoothSdk.types.js.map +1 -1
  39. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  40. package/build/_private/BluetoothSdkModule.js +4 -0
  41. package/build/_private/BluetoothSdkModule.js.map +1 -1
  42. package/build/_private/nativeLogConsole.d.ts +17 -0
  43. package/build/_private/nativeLogConsole.d.ts.map +1 -0
  44. package/build/_private/nativeLogConsole.js +16 -0
  45. package/build/_private/nativeLogConsole.js.map +1 -0
  46. package/build/generated/releaseMetadata.js +5 -5
  47. package/build/generated/releaseMetadata.js.map +1 -1
  48. package/build/react/useGlassesConnection.d.ts +5 -0
  49. package/build/react/useGlassesConnection.d.ts.map +1 -1
  50. package/build/react/useGlassesConnection.js +2 -2
  51. package/build/react/useGlassesConnection.js.map +1 -1
  52. package/build/react/useMentraBluetooth.d.ts +5 -0
  53. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  54. package/build/react/useMentraBluetooth.js +1 -0
  55. package/build/react/useMentraBluetooth.js.map +1 -1
  56. package/ios/BluetoothSdkModule.swift +2 -2
  57. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  58. package/ios/Source/Bridge.swift +3 -2
  59. package/ios/Source/DeviceManager.swift +4 -2
  60. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  61. package/ios/Source/MentraBluetoothSDK.swift +10 -3
  62. package/ios/Source/internal/BleTraceLogger.swift +3 -0
  63. package/ios/Source/sgcs/G1.swift +1 -1
  64. package/ios/Source/sgcs/MentraLive.swift +28 -11
  65. package/ios/Source/stt/STTTools.swift +1 -1
  66. package/ios/Source/types/DeviceModels.swift +9 -1
  67. package/ios/Source/utils/G1Text.swift +1 -1
  68. package/ios/Source/utils/JSCExperiment.swift +1 -2
  69. package/ios/Source/utils/MessageChunkReassembler.swift +6 -6
  70. package/ios/Source/utils/MessageChunker.swift +8 -8
  71. package/ios/Source/utils/Models.swift +1 -1
  72. package/ios/Tests/MentraLiveConnectionOptionsTests.swift +30 -0
  73. package/ios/Tests/NativeLogTests.swift +34 -0
  74. package/package.json +1 -1
  75. package/src/BluetoothSdk.types.ts +7 -0
  76. package/src/_private/BluetoothSdkModule.ts +5 -0
  77. package/src/_private/nativeLogConsole.ts +24 -0
  78. package/src/generated/releaseMetadata.ts +5 -5
  79. package/src/react/useGlassesConnection.ts +7 -2
  80. package/src/react/useMentraBluetooth.ts +6 -0
@@ -10,6 +10,11 @@ export type UseGlassesConnectionOptions = {
10
10
  autoConnectDefault?: boolean;
11
11
  defaultDeviceStorage?: DefaultDeviceStorage;
12
12
  onError?: (error: unknown) => void;
13
+ /**
14
+ * Whether iOS should require ANCS authorization when automatically connecting.
15
+ * Defaults to true. Android accepts this option as a no-op.
16
+ */
17
+ requiresAncs?: boolean;
13
18
  scanModel?: DeviceModel;
14
19
  scanTimeoutMs?: number;
15
20
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useGlassesConnection.d.ts","sourceRoot":"","sources":["../../src/react/useGlassesConnection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAA;AAE9E,OAAO,EAAmB,KAAK,uBAAuB,EAAC,MAAM,oBAAoB,CAAA;AACjF,OAAO,EAAqB,KAAK,yBAAyB,EAAC,MAAM,sBAAsB,CAAA;AAEvF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAClC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C,CAAA;AAED,MAAM,MAAM,uBAAuB,GAC/B,MAAM,GACN,UAAU,GACV,YAAY,GACZ,eAAe,GACf,YAAY,CAAA;AAEhB,MAAM,MAAM,2BAA2B,GAAG;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,SAAS,CAAC,EAAE,WAAW,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,GAAG;IACpE,MAAM,EAAE,uBAAuB,CAAA;IAC/B,IAAI,EAAE,OAAO,CAAA;IACb,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,EAAE,uBAAuB,CAAA;IAC7B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3D,CAAA;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC,GACxC,2BAA2B,CAqK7B"}
1
+ {"version":3,"file":"useGlassesConnection.d.ts","sourceRoot":"","sources":["../../src/react/useGlassesConnection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAA;AAE9E,OAAO,EAAmB,KAAK,uBAAuB,EAAC,MAAM,oBAAoB,CAAA;AACjF,OAAO,EAAqB,KAAK,yBAAyB,EAAC,MAAM,sBAAsB,CAAA;AAEvF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAClC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C,CAAA;AAED,MAAM,MAAM,uBAAuB,GAC/B,MAAM,GACN,UAAU,GACV,YAAY,GACZ,eAAe,GACf,YAAY,CAAA;AAEhB,MAAM,MAAM,2BAA2B,GAAG;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,WAAW,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,GAAG;IACpE,MAAM,EAAE,uBAAuB,CAAA;IAC/B,IAAI,EAAE,OAAO,CAAA;IACb,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,EAAE,uBAAuB,CAAA;IAC7B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3D,CAAA;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC,GACxC,2BAA2B,CAqK7B"}
@@ -70,8 +70,8 @@ export function useGlassesConnection(options = {}) {
70
70
  return;
71
71
  }
72
72
  autoConnectAttemptedRef.current = true;
73
- void connectDefault().catch(() => undefined);
74
- }, [defaultDevice, options.autoConnectDefault, status.connected]);
73
+ void connectDefault({ requiresAncs: options.requiresAncs ?? true }).catch(() => undefined);
74
+ }, [defaultDevice, options.autoConnectDefault, options.requiresAncs, status.connected]);
75
75
  async function runConnectionAction(nextAction, operation) {
76
76
  setAction(nextAction);
77
77
  setOperationError(null);
@@ -1 +1 @@
1
- {"version":3,"file":"useGlassesConnection.js","sourceRoot":"","sources":["../../src/react/useGlassesConnection.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef, useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport type {ConnectOptions, Device, DeviceModel} from \"../BluetoothSdk.types\"\n\nimport {useBluetoothScan, type BluetoothScanHookResult} from \"./useBluetoothScan\"\nimport {useBluetoothStatus, type BluetoothStatusHookResult} from \"./useBluetoothStatus\"\n\nexport type DefaultDeviceStorage = {\n load: () => Promise<Device | null>\n save: (device: Device | null) => Promise<void>\n}\n\nexport type GlassesConnectionAction =\n | \"idle\"\n | \"scanning\"\n | \"connecting\"\n | \"disconnecting\"\n | \"forgetting\"\n\nexport type UseGlassesConnectionOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n onError?: (error: unknown) => void\n scanModel?: DeviceModel\n scanTimeoutMs?: number\n}\n\nexport type GlassesConnectionHookResult = BluetoothStatusHookResult & {\n action: GlassesConnectionAction\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n forget: () => Promise<void>\n scan: BluetoothScanHookResult\n setDefaultDevice: (device: Device | null) => Promise<void>\n}\n\nexport function useGlassesConnection(\n options: UseGlassesConnectionOptions = {},\n): GlassesConnectionHookResult {\n const status = useBluetoothStatus({onError: options.onError})\n const scan = useBluetoothScan({\n model: options.scanModel,\n onError: options.onError,\n timeoutMs: options.scanTimeoutMs,\n })\n const [action, setAction] = useState<GlassesConnectionAction>(\"idle\")\n const [defaultDevice, setDefaultDeviceState] = useState<Device | null>(null)\n const [operationError, setOperationError] = useState<unknown | null>(null)\n const autoConnectAttemptedRef = useRef(false)\n const defaultDeviceStorageRef = useRef(options.defaultDeviceStorage)\n const onErrorRef = useRef(options.onError)\n\n useEffect(() => {\n defaultDeviceStorageRef.current = options.defaultDeviceStorage\n onErrorRef.current = options.onError\n }, [options.defaultDeviceStorage, options.onError])\n\n useEffect(() => {\n let mounted = true\n\n const loadDefaultDevice = async () => {\n try {\n const [nativeDefaultDevice, storedDefaultDevice] = await Promise.all([\n BluetoothSdk.getDefaultDevice(),\n defaultDeviceStorageRef.current?.load() ?? Promise.resolve(null),\n ])\n if (!mounted) {\n return\n }\n const nextDefaultDevice = storedDefaultDevice ?? nativeDefaultDevice\n if (nextDefaultDevice) {\n await BluetoothSdk.setDefaultDevice(nextDefaultDevice)\n }\n if (mounted) {\n setDefaultDeviceState(nextDefaultDevice)\n setOperationError(null)\n }\n } catch (nextError) {\n if (!mounted) {\n return\n }\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n }\n }\n\n void loadDefaultDevice()\n\n return () => {\n mounted = false\n }\n }, [])\n\n useEffect(() => {\n const subscription = BluetoothSdk.addListener(\"default_device_changed\", (event) => {\n const nextDefaultDevice = event.device ?? null\n setDefaultDeviceState(nextDefaultDevice)\n void defaultDeviceStorageRef.current?.save(nextDefaultDevice).catch((nextError) => {\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n })\n })\n\n return () => {\n subscription.remove()\n }\n }, [])\n\n useEffect(() => {\n if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {\n return\n }\n\n autoConnectAttemptedRef.current = true\n void connectDefault().catch(() => undefined)\n }, [defaultDevice, options.autoConnectDefault, status.connected])\n\n async function runConnectionAction<T>(\n nextAction: Exclude<GlassesConnectionAction, \"idle\" | \"scanning\">,\n operation: () => Promise<T>,\n ): Promise<T> {\n setAction(nextAction)\n setOperationError(null)\n try {\n const result = await operation()\n setOperationError(null)\n return result\n } catch (nextError) {\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n throw nextError\n } finally {\n setAction(\"idle\")\n }\n }\n\n async function setDefaultDevice(device: Device | null) {\n await runConnectionAction(\"connecting\", async () => {\n await BluetoothSdk.setDefaultDevice(device)\n await defaultDeviceStorageRef.current?.save(device)\n setDefaultDeviceState(device)\n })\n }\n\n async function clearDefaultDevice() {\n await runConnectionAction(\"forgetting\", async () => {\n await BluetoothSdk.clearDefaultDevice()\n await defaultDeviceStorageRef.current?.save(null)\n setDefaultDeviceState(null)\n scan.selectDevice(null)\n })\n }\n\n async function connect(device?: Device, connectOptions?: ConnectOptions) {\n await runConnectionAction(\"connecting\", async () => {\n const targetDevice = device ?? scan.selectedDevice\n if (targetDevice) {\n await BluetoothSdk.connect(targetDevice, connectOptions)\n if (connectOptions?.saveAsDefault !== false) {\n await defaultDeviceStorageRef.current?.save(targetDevice)\n setDefaultDeviceState(targetDevice)\n }\n return\n }\n await BluetoothSdk.connectDefault(connectOptions)\n })\n }\n\n async function connectDefault(connectOptions?: ConnectOptions) {\n await runConnectionAction(\"connecting\", async () => {\n await BluetoothSdk.connectDefault(connectOptions)\n })\n }\n\n async function disconnect() {\n await runConnectionAction(\"disconnecting\", async () => {\n await BluetoothSdk.disconnect()\n })\n }\n\n async function forget() {\n await runConnectionAction(\"forgetting\", async () => {\n await BluetoothSdk.forget()\n scan.clearResults()\n })\n }\n\n const busy = action !== \"idle\" || scan.scanning || status.loading\n\n return {\n ...status,\n action: scan.scanning ? \"scanning\" : action,\n busy,\n clearDefaultDevice,\n connect,\n connectDefault,\n defaultDevice,\n disconnect,\n error: operationError ?? scan.error ?? status.error,\n forget,\n scan,\n setDefaultDevice,\n }\n}\n"]}
1
+ {"version":3,"file":"useGlassesConnection.js","sourceRoot":"","sources":["../../src/react/useGlassesConnection.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef, useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport type {ConnectOptions, Device, DeviceModel} from \"../BluetoothSdk.types\"\n\nimport {useBluetoothScan, type BluetoothScanHookResult} from \"./useBluetoothScan\"\nimport {useBluetoothStatus, type BluetoothStatusHookResult} from \"./useBluetoothStatus\"\n\nexport type DefaultDeviceStorage = {\n load: () => Promise<Device | null>\n save: (device: Device | null) => Promise<void>\n}\n\nexport type GlassesConnectionAction =\n | \"idle\"\n | \"scanning\"\n | \"connecting\"\n | \"disconnecting\"\n | \"forgetting\"\n\nexport type UseGlassesConnectionOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n onError?: (error: unknown) => void\n /**\n * Whether iOS should require ANCS authorization when automatically connecting.\n * Defaults to true. Android accepts this option as a no-op.\n */\n requiresAncs?: boolean\n scanModel?: DeviceModel\n scanTimeoutMs?: number\n}\n\nexport type GlassesConnectionHookResult = BluetoothStatusHookResult & {\n action: GlassesConnectionAction\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n forget: () => Promise<void>\n scan: BluetoothScanHookResult\n setDefaultDevice: (device: Device | null) => Promise<void>\n}\n\nexport function useGlassesConnection(\n options: UseGlassesConnectionOptions = {},\n): GlassesConnectionHookResult {\n const status = useBluetoothStatus({onError: options.onError})\n const scan = useBluetoothScan({\n model: options.scanModel,\n onError: options.onError,\n timeoutMs: options.scanTimeoutMs,\n })\n const [action, setAction] = useState<GlassesConnectionAction>(\"idle\")\n const [defaultDevice, setDefaultDeviceState] = useState<Device | null>(null)\n const [operationError, setOperationError] = useState<unknown | null>(null)\n const autoConnectAttemptedRef = useRef(false)\n const defaultDeviceStorageRef = useRef(options.defaultDeviceStorage)\n const onErrorRef = useRef(options.onError)\n\n useEffect(() => {\n defaultDeviceStorageRef.current = options.defaultDeviceStorage\n onErrorRef.current = options.onError\n }, [options.defaultDeviceStorage, options.onError])\n\n useEffect(() => {\n let mounted = true\n\n const loadDefaultDevice = async () => {\n try {\n const [nativeDefaultDevice, storedDefaultDevice] = await Promise.all([\n BluetoothSdk.getDefaultDevice(),\n defaultDeviceStorageRef.current?.load() ?? Promise.resolve(null),\n ])\n if (!mounted) {\n return\n }\n const nextDefaultDevice = storedDefaultDevice ?? nativeDefaultDevice\n if (nextDefaultDevice) {\n await BluetoothSdk.setDefaultDevice(nextDefaultDevice)\n }\n if (mounted) {\n setDefaultDeviceState(nextDefaultDevice)\n setOperationError(null)\n }\n } catch (nextError) {\n if (!mounted) {\n return\n }\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n }\n }\n\n void loadDefaultDevice()\n\n return () => {\n mounted = false\n }\n }, [])\n\n useEffect(() => {\n const subscription = BluetoothSdk.addListener(\"default_device_changed\", (event) => {\n const nextDefaultDevice = event.device ?? null\n setDefaultDeviceState(nextDefaultDevice)\n void defaultDeviceStorageRef.current?.save(nextDefaultDevice).catch((nextError) => {\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n })\n })\n\n return () => {\n subscription.remove()\n }\n }, [])\n\n useEffect(() => {\n if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {\n return\n }\n\n autoConnectAttemptedRef.current = true\n void connectDefault({requiresAncs: options.requiresAncs ?? true}).catch(() => undefined)\n }, [defaultDevice, options.autoConnectDefault, options.requiresAncs, status.connected])\n\n async function runConnectionAction<T>(\n nextAction: Exclude<GlassesConnectionAction, \"idle\" | \"scanning\">,\n operation: () => Promise<T>,\n ): Promise<T> {\n setAction(nextAction)\n setOperationError(null)\n try {\n const result = await operation()\n setOperationError(null)\n return result\n } catch (nextError) {\n setOperationError(nextError)\n onErrorRef.current?.(nextError)\n throw nextError\n } finally {\n setAction(\"idle\")\n }\n }\n\n async function setDefaultDevice(device: Device | null) {\n await runConnectionAction(\"connecting\", async () => {\n await BluetoothSdk.setDefaultDevice(device)\n await defaultDeviceStorageRef.current?.save(device)\n setDefaultDeviceState(device)\n })\n }\n\n async function clearDefaultDevice() {\n await runConnectionAction(\"forgetting\", async () => {\n await BluetoothSdk.clearDefaultDevice()\n await defaultDeviceStorageRef.current?.save(null)\n setDefaultDeviceState(null)\n scan.selectDevice(null)\n })\n }\n\n async function connect(device?: Device, connectOptions?: ConnectOptions) {\n await runConnectionAction(\"connecting\", async () => {\n const targetDevice = device ?? scan.selectedDevice\n if (targetDevice) {\n await BluetoothSdk.connect(targetDevice, connectOptions)\n if (connectOptions?.saveAsDefault !== false) {\n await defaultDeviceStorageRef.current?.save(targetDevice)\n setDefaultDeviceState(targetDevice)\n }\n return\n }\n await BluetoothSdk.connectDefault(connectOptions)\n })\n }\n\n async function connectDefault(connectOptions?: ConnectOptions) {\n await runConnectionAction(\"connecting\", async () => {\n await BluetoothSdk.connectDefault(connectOptions)\n })\n }\n\n async function disconnect() {\n await runConnectionAction(\"disconnecting\", async () => {\n await BluetoothSdk.disconnect()\n })\n }\n\n async function forget() {\n await runConnectionAction(\"forgetting\", async () => {\n await BluetoothSdk.forget()\n scan.clearResults()\n })\n }\n\n const busy = action !== \"idle\" || scan.scanning || status.loading\n\n return {\n ...status,\n action: scan.scanning ? \"scanning\" : action,\n busy,\n clearDefaultDevice,\n connect,\n connectDefault,\n defaultDevice,\n disconnect,\n error: operationError ?? scan.error ?? status.error,\n forget,\n scan,\n setDefaultDevice,\n }\n}\n"]}
@@ -79,6 +79,11 @@ export type UseMentraBluetoothOptions = {
79
79
  defaultDeviceStorage?: DefaultDeviceStorage;
80
80
  defaultModel?: DeviceModel;
81
81
  onError?: (error: unknown) => void;
82
+ /**
83
+ * Whether iOS should require ANCS authorization when automatically connecting.
84
+ * Defaults to true. Android accepts this option as a no-op.
85
+ */
86
+ requiresAncs?: boolean;
82
87
  scanTimeoutMs?: number;
83
88
  };
84
89
  export type MentraBluetoothSession = {
@@ -1 +1 @@
1
- {"version":3,"file":"useMentraBluetooth.d.ts","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useMentraBluetooth.d.ts","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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"}
@@ -114,6 +114,7 @@ export function useMentraBluetooth(options = {}) {
114
114
  autoConnectDefault: options.autoConnectDefault,
115
115
  defaultDeviceStorage: options.defaultDeviceStorage,
116
116
  onError: options.onError,
117
+ requiresAncs: options.requiresAncs,
117
118
  scanModel: options.defaultModel ?? DeviceModels.MentraLive,
118
119
  scanTimeoutMs: options.scanTimeoutMs,
119
120
  });
@@ -1 +1 @@
1
- {"version":3,"file":"useMentraBluetooth.js","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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","sourcesContent":["import {useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport {\n DEFAULT_VOICE_ACTIVITY_DETECTION_ENABLED,\n DeviceModels,\n createDisconnectedGlassesStatus,\n isConnectedGlassesConnectionStatus,\n isReadyGlassesConnectionStatus,\n} from \"../BluetoothSdk.types\"\nimport type {\n ConnectOptions,\n Device,\n DeviceModel,\n GlassesConnectionStatus,\n HotspotStatus,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n MicMode,\n SettingsAckSuccessEvent,\n WifiSearchResult,\n WifiStatus,\n} from \"../BluetoothSdk.types\"\n\nimport {\n useGlassesConnection,\n type DefaultDeviceStorage,\n type GlassesConnectionHookResult,\n} from \"./useGlassesConnection\"\n\nexport type BatteryState = {\n charging: boolean\n level: number | null\n}\n\nexport type ConnectedGlassesInfo = {\n appVersion?: string\n bluetoothName?: string\n buildNumber?: string\n color?: string\n deviceModel?: string\n firmwareVersion?: string\n serialNumber?: string\n style?: string\n}\n\nexport type FirmwareInfo = {\n appVersion?: string\n buildNumber?: string\n source:\n | \"app\"\n | \"bes\"\n | \"device\"\n | \"firmware\"\n | \"left\"\n | \"mtk\"\n | \"right\"\n | \"unknown\"\n version: string | null\n}\n\nexport type SignalState = {\n strengthDbm: number | null\n updatedAt: number | null\n}\n\nexport type GlassesRuntimeState =\n | {\n connected: false\n connection: Exclude<GlassesConnectionStatus, {state: \"connected\"}>\n ready: false\n }\n | {\n battery: BatteryState\n connected: true\n connection: Extract<GlassesConnectionStatus, {state: \"connected\"}>\n device: ConnectedGlassesInfo\n firmware: FirmwareInfo\n hotspot: HotspotStatus\n ready: boolean\n signal: SignalState\n voiceActivityDetectionEnabled: boolean\n wifi: WifiStatus\n }\n\nexport type GalleryModeState = {\n applying: boolean\n enabled: boolean\n error: unknown | null\n}\n\nexport type PhoneSdkRuntimeState = {\n currentMic: MicMode | null\n defaultDevice: Device | null\n galleryMode: GalleryModeState\n lastLog: string[]\n micRanking: MicMode[]\n otherBluetoothConnected: boolean\n searching: boolean\n searchingController: boolean\n systemMicUnavailable: boolean\n wifiScanResults: WifiSearchResult[]\n}\n\nexport type ScanController = {\n active: boolean\n clear: () => void\n devices: Device[]\n diagnostic: GlassesConnectionHookResult[\"scan\"][\"diagnostic\"]\n error: unknown | null\n model: DeviceModel\n selectedDevice: Device | null\n selectDevice: (device: Device | null) => void\n setModel: (model: DeviceModel) => void\n start: (model?: DeviceModel) => Promise<Device[]>\n stop: () => Promise<void>\n}\n\nexport type UseMentraBluetoothOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n defaultModel?: DeviceModel\n onError?: (error: unknown) => void\n scanTimeoutMs?: number\n}\n\nexport type MentraBluetoothSession = {\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n error: unknown | null\n glasses: GlassesRuntimeState\n refresh: () => Promise<void>\n scan: ScanController\n sdk: PhoneSdkRuntimeState\n setDefaultDevice: (device: Device | null) => Promise<void>\n setGalleryModeEnabled: (enabled: boolean) => Promise<SettingsAckSuccessEvent>\n setVoiceActivityDetectionEnabled: (enabled: boolean) => Promise<void>\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined\n}\n\nfunction numberValue(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null\n}\n\nfunction firmwareInfo(status: Partial<PublicGlassesStatus>): FirmwareInfo {\n const firmwareSources = [\n [\"firmwareVersion\", \"firmware\"],\n [\"deviceFirmwareVersion\", \"device\"],\n [\"rightFirmwareVersion\", \"right\"],\n [\"leftFirmwareVersion\", \"left\"],\n [\"besFirmwareVersion\", \"bes\"],\n [\"mtkFirmwareVersion\", \"mtk\"],\n [\"appVersion\", \"app\"],\n ] as const\n\n for (const [key, source] of firmwareSources) {\n const value = stringValue((status as Record<string, unknown>)[key])\n if (value) {\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source,\n version: value,\n }\n }\n }\n\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source: \"unknown\",\n version: null,\n }\n}\n\nfunction batteryState(status: Partial<PublicGlassesStatus>): BatteryState {\n const level = typeof status.batteryLevel === \"number\" && status.batteryLevel >= 0 ? status.batteryLevel : null\n return {\n charging: status.charging ?? false,\n level,\n }\n}\n\nfunction connectedGlassesInfo(status: Partial<PublicGlassesStatus>): ConnectedGlassesInfo {\n return {\n appVersion: stringValue(status.appVersion),\n bluetoothName: stringValue(status.bluetoothName),\n buildNumber: stringValue(status.buildNumber),\n color: stringValue(status.color),\n deviceModel: stringValue(status.deviceModel),\n firmwareVersion: stringValue(status.firmwareVersion),\n serialNumber: stringValue(status.serialNumber),\n style: stringValue(status.style),\n }\n}\n\nfunction runtimeGlassesState(status: Partial<PublicGlassesStatus>): GlassesRuntimeState {\n const connection = status.connection ?? createDisconnectedGlassesStatus().connection ?? {state: \"disconnected\"}\n\n if (!isConnectedGlassesConnectionStatus(connection)) {\n return {\n connected: false,\n connection,\n ready: false,\n }\n }\n\n return {\n battery: batteryState(status),\n connected: true,\n connection,\n device: connectedGlassesInfo(status),\n firmware: firmwareInfo(status),\n hotspot: status.hotspot ?? {state: \"disabled\"},\n ready: isReadyGlassesConnectionStatus(connection),\n signal: {\n strengthDbm: numberValue((status as Record<string, unknown>).signalStrength),\n updatedAt: numberValue((status as Record<string, unknown>).signalStrengthUpdatedAt),\n },\n voiceActivityDetectionEnabled: status.voiceActivityDetectionEnabled ?? DEFAULT_VOICE_ACTIVITY_DETECTION_ENABLED,\n wifi: status.wifi ?? {state: \"disconnected\"},\n }\n}\n\nfunction phoneSdkState(\n status: Partial<PublicBluetoothStatus>,\n defaultDevice: Device | null,\n galleryMode: GalleryModeState,\n): PhoneSdkRuntimeState {\n return {\n currentMic: status.currentMic || null,\n defaultDevice,\n galleryMode,\n lastLog: status.lastLog ?? [],\n micRanking: status.micRanking ?? [],\n otherBluetoothConnected: status.otherBtConnected ?? false,\n searching: status.searching ?? false,\n searchingController: status.searchingController ?? false,\n systemMicUnavailable: status.systemMicUnavailable ?? false,\n wifiScanResults: status.wifiScanResults ?? [],\n }\n}\n\nfunction scanController(connection: GlassesConnectionHookResult): ScanController {\n return {\n active: connection.scan.scanning,\n clear: connection.scan.clearResults,\n devices: connection.scan.devices,\n diagnostic: connection.scan.diagnostic,\n error: connection.scan.error,\n model: connection.scan.model,\n selectedDevice: connection.scan.selectedDevice,\n selectDevice: connection.scan.selectDevice,\n setModel: connection.scan.setModel,\n start: connection.scan.startScan,\n stop: connection.scan.stopScan,\n }\n}\n\nexport function useMentraBluetooth(options: UseMentraBluetoothOptions = {}): MentraBluetoothSession {\n const connection = useGlassesConnection({\n autoConnectDefault: options.autoConnectDefault,\n defaultDeviceStorage: options.defaultDeviceStorage,\n onError: options.onError,\n scanModel: options.defaultModel ?? DeviceModels.MentraLive,\n scanTimeoutMs: options.scanTimeoutMs,\n })\n const [galleryModeApplying, setGalleryModeApplying] = useState(false)\n const [galleryModeError, setGalleryModeError] = useState<unknown | null>(null)\n\n async function setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent> {\n setGalleryModeApplying(true)\n setGalleryModeError(null)\n try {\n return await BluetoothSdk.setGalleryModeEnabled(enabled)\n } catch (error) {\n setGalleryModeError(error)\n options.onError?.(error)\n throw error\n } finally {\n setGalleryModeApplying(false)\n }\n }\n\n async function setVoiceActivityDetectionEnabled(enabled: boolean) {\n try {\n await BluetoothSdk.setVoiceActivityDetectionEnabled(enabled)\n } catch (error) {\n options.onError?.(error)\n throw error\n }\n }\n\n const galleryMode: GalleryModeState = {\n applying: galleryModeApplying,\n enabled: connection.bluetoothStatus.galleryModeEnabled !== false,\n error: galleryModeError,\n }\n\n return {\n busy: connection.busy || galleryModeApplying,\n clearDefaultDevice: connection.clearDefaultDevice,\n connect: connection.connect,\n connectDefault: connection.connectDefault,\n defaultDevice: connection.defaultDevice,\n disconnect: connection.disconnect,\n error: galleryModeError ?? connection.error,\n glasses: runtimeGlassesState(connection.glassesStatus),\n refresh: connection.refresh,\n scan: scanController(connection),\n sdk: phoneSdkState(connection.bluetoothStatus, connection.defaultDevice, galleryMode),\n setDefaultDevice: connection.setDefaultDevice,\n setGalleryModeEnabled,\n setVoiceActivityDetectionEnabled,\n }\n}\n"]}
1
+ {"version":3,"file":"useMentraBluetooth.js","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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","sourcesContent":["import {useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport {\n DEFAULT_VOICE_ACTIVITY_DETECTION_ENABLED,\n DeviceModels,\n createDisconnectedGlassesStatus,\n isConnectedGlassesConnectionStatus,\n isReadyGlassesConnectionStatus,\n} from \"../BluetoothSdk.types\"\nimport type {\n ConnectOptions,\n Device,\n DeviceModel,\n GlassesConnectionStatus,\n HotspotStatus,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n MicMode,\n SettingsAckSuccessEvent,\n WifiSearchResult,\n WifiStatus,\n} from \"../BluetoothSdk.types\"\n\nimport {\n useGlassesConnection,\n type DefaultDeviceStorage,\n type GlassesConnectionHookResult,\n} from \"./useGlassesConnection\"\n\nexport type BatteryState = {\n charging: boolean\n level: number | null\n}\n\nexport type ConnectedGlassesInfo = {\n appVersion?: string\n bluetoothName?: string\n buildNumber?: string\n color?: string\n deviceModel?: string\n firmwareVersion?: string\n serialNumber?: string\n style?: string\n}\n\nexport type FirmwareInfo = {\n appVersion?: string\n buildNumber?: string\n source:\n | \"app\"\n | \"bes\"\n | \"device\"\n | \"firmware\"\n | \"left\"\n | \"mtk\"\n | \"right\"\n | \"unknown\"\n version: string | null\n}\n\nexport type SignalState = {\n strengthDbm: number | null\n updatedAt: number | null\n}\n\nexport type GlassesRuntimeState =\n | {\n connected: false\n connection: Exclude<GlassesConnectionStatus, {state: \"connected\"}>\n ready: false\n }\n | {\n battery: BatteryState\n connected: true\n connection: Extract<GlassesConnectionStatus, {state: \"connected\"}>\n device: ConnectedGlassesInfo\n firmware: FirmwareInfo\n hotspot: HotspotStatus\n ready: boolean\n signal: SignalState\n voiceActivityDetectionEnabled: boolean\n wifi: WifiStatus\n }\n\nexport type GalleryModeState = {\n applying: boolean\n enabled: boolean\n error: unknown | null\n}\n\nexport type PhoneSdkRuntimeState = {\n currentMic: MicMode | null\n defaultDevice: Device | null\n galleryMode: GalleryModeState\n lastLog: string[]\n micRanking: MicMode[]\n otherBluetoothConnected: boolean\n searching: boolean\n searchingController: boolean\n systemMicUnavailable: boolean\n wifiScanResults: WifiSearchResult[]\n}\n\nexport type ScanController = {\n active: boolean\n clear: () => void\n devices: Device[]\n diagnostic: GlassesConnectionHookResult[\"scan\"][\"diagnostic\"]\n error: unknown | null\n model: DeviceModel\n selectedDevice: Device | null\n selectDevice: (device: Device | null) => void\n setModel: (model: DeviceModel) => void\n start: (model?: DeviceModel) => Promise<Device[]>\n stop: () => Promise<void>\n}\n\nexport type UseMentraBluetoothOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n defaultModel?: DeviceModel\n onError?: (error: unknown) => void\n /**\n * Whether iOS should require ANCS authorization when automatically connecting.\n * Defaults to true. Android accepts this option as a no-op.\n */\n requiresAncs?: boolean\n scanTimeoutMs?: number\n}\n\nexport type MentraBluetoothSession = {\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n error: unknown | null\n glasses: GlassesRuntimeState\n refresh: () => Promise<void>\n scan: ScanController\n sdk: PhoneSdkRuntimeState\n setDefaultDevice: (device: Device | null) => Promise<void>\n setGalleryModeEnabled: (enabled: boolean) => Promise<SettingsAckSuccessEvent>\n setVoiceActivityDetectionEnabled: (enabled: boolean) => Promise<void>\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined\n}\n\nfunction numberValue(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null\n}\n\nfunction firmwareInfo(status: Partial<PublicGlassesStatus>): FirmwareInfo {\n const firmwareSources = [\n [\"firmwareVersion\", \"firmware\"],\n [\"deviceFirmwareVersion\", \"device\"],\n [\"rightFirmwareVersion\", \"right\"],\n [\"leftFirmwareVersion\", \"left\"],\n [\"besFirmwareVersion\", \"bes\"],\n [\"mtkFirmwareVersion\", \"mtk\"],\n [\"appVersion\", \"app\"],\n ] as const\n\n for (const [key, source] of firmwareSources) {\n const value = stringValue((status as Record<string, unknown>)[key])\n if (value) {\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source,\n version: value,\n }\n }\n }\n\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source: \"unknown\",\n version: null,\n }\n}\n\nfunction batteryState(status: Partial<PublicGlassesStatus>): BatteryState {\n const level = typeof status.batteryLevel === \"number\" && status.batteryLevel >= 0 ? status.batteryLevel : null\n return {\n charging: status.charging ?? false,\n level,\n }\n}\n\nfunction connectedGlassesInfo(status: Partial<PublicGlassesStatus>): ConnectedGlassesInfo {\n return {\n appVersion: stringValue(status.appVersion),\n bluetoothName: stringValue(status.bluetoothName),\n buildNumber: stringValue(status.buildNumber),\n color: stringValue(status.color),\n deviceModel: stringValue(status.deviceModel),\n firmwareVersion: stringValue(status.firmwareVersion),\n serialNumber: stringValue(status.serialNumber),\n style: stringValue(status.style),\n }\n}\n\nfunction runtimeGlassesState(status: Partial<PublicGlassesStatus>): GlassesRuntimeState {\n const connection = status.connection ?? createDisconnectedGlassesStatus().connection ?? {state: \"disconnected\"}\n\n if (!isConnectedGlassesConnectionStatus(connection)) {\n return {\n connected: false,\n connection,\n ready: false,\n }\n }\n\n return {\n battery: batteryState(status),\n connected: true,\n connection,\n device: connectedGlassesInfo(status),\n firmware: firmwareInfo(status),\n hotspot: status.hotspot ?? {state: \"disabled\"},\n ready: isReadyGlassesConnectionStatus(connection),\n signal: {\n strengthDbm: numberValue((status as Record<string, unknown>).signalStrength),\n updatedAt: numberValue((status as Record<string, unknown>).signalStrengthUpdatedAt),\n },\n voiceActivityDetectionEnabled: status.voiceActivityDetectionEnabled ?? DEFAULT_VOICE_ACTIVITY_DETECTION_ENABLED,\n wifi: status.wifi ?? {state: \"disconnected\"},\n }\n}\n\nfunction phoneSdkState(\n status: Partial<PublicBluetoothStatus>,\n defaultDevice: Device | null,\n galleryMode: GalleryModeState,\n): PhoneSdkRuntimeState {\n return {\n currentMic: status.currentMic || null,\n defaultDevice,\n galleryMode,\n lastLog: status.lastLog ?? [],\n micRanking: status.micRanking ?? [],\n otherBluetoothConnected: status.otherBtConnected ?? false,\n searching: status.searching ?? false,\n searchingController: status.searchingController ?? false,\n systemMicUnavailable: status.systemMicUnavailable ?? false,\n wifiScanResults: status.wifiScanResults ?? [],\n }\n}\n\nfunction scanController(connection: GlassesConnectionHookResult): ScanController {\n return {\n active: connection.scan.scanning,\n clear: connection.scan.clearResults,\n devices: connection.scan.devices,\n diagnostic: connection.scan.diagnostic,\n error: connection.scan.error,\n model: connection.scan.model,\n selectedDevice: connection.scan.selectedDevice,\n selectDevice: connection.scan.selectDevice,\n setModel: connection.scan.setModel,\n start: connection.scan.startScan,\n stop: connection.scan.stopScan,\n }\n}\n\nexport function useMentraBluetooth(options: UseMentraBluetoothOptions = {}): MentraBluetoothSession {\n const connection = useGlassesConnection({\n autoConnectDefault: options.autoConnectDefault,\n defaultDeviceStorage: options.defaultDeviceStorage,\n onError: options.onError,\n requiresAncs: options.requiresAncs,\n scanModel: options.defaultModel ?? DeviceModels.MentraLive,\n scanTimeoutMs: options.scanTimeoutMs,\n })\n const [galleryModeApplying, setGalleryModeApplying] = useState(false)\n const [galleryModeError, setGalleryModeError] = useState<unknown | null>(null)\n\n async function setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent> {\n setGalleryModeApplying(true)\n setGalleryModeError(null)\n try {\n return await BluetoothSdk.setGalleryModeEnabled(enabled)\n } catch (error) {\n setGalleryModeError(error)\n options.onError?.(error)\n throw error\n } finally {\n setGalleryModeApplying(false)\n }\n }\n\n async function setVoiceActivityDetectionEnabled(enabled: boolean) {\n try {\n await BluetoothSdk.setVoiceActivityDetectionEnabled(enabled)\n } catch (error) {\n options.onError?.(error)\n throw error\n }\n }\n\n const galleryMode: GalleryModeState = {\n applying: galleryModeApplying,\n enabled: connection.bluetoothStatus.galleryModeEnabled !== false,\n error: galleryModeError,\n }\n\n return {\n busy: connection.busy || galleryModeApplying,\n clearDefaultDevice: connection.clearDefaultDevice,\n connect: connection.connect,\n connectDefault: connection.connectDefault,\n defaultDevice: connection.defaultDevice,\n disconnect: connection.disconnect,\n error: galleryModeError ?? connection.error,\n glasses: runtimeGlassesState(connection.glassesStatus),\n refresh: connection.refresh,\n scan: scanController(connection),\n sdk: phoneSdkState(connection.bluetoothStatus, connection.defaultDevice, galleryMode),\n setDefaultDevice: connection.setDefaultDevice,\n setGalleryModeEnabled,\n setVoiceActivityDetectionEnabled,\n }\n}\n"]}
@@ -966,9 +966,9 @@ private extension ConnectOptions {
966
966
  init(dictionary values: [String: Any]?) {
967
967
  self.init(
968
968
  saveAsDefault: values?["saveAsDefault"] as? Bool ?? true,
969
- cancelExistingConnectionAttempt: values?["cancelExistingConnectionAttempt"] as? Bool ?? true
969
+ cancelExistingConnectionAttempt: values?["cancelExistingConnectionAttempt"] as? Bool ?? true,
970
+ requiresAncs: values?["requiresAncs"] as? Bool ?? true
970
971
  )
971
972
  }
972
973
  }
973
974
 
974
-
@@ -11,7 +11,7 @@ enum BluetoothSdkDefaults {
11
11
  static let voiceActivityDetectionEnabled = false
12
12
  static let loudnessGateEnabled = false
13
13
  private static let infoSdkVersionKey = "MentraBluetoothSdkVersion"
14
- private static let swiftPackageSdkVersion = "3.2.0-dev.191"
14
+ private static let swiftPackageSdkVersion = "3.2.0-dev.195"
15
15
  private static let swiftPackageSdkVersionPlaceholder = "__MENTRA" + "_BLUETOOTH_SDK_VERSION__"
16
16
 
17
17
  private static func normalizedSdkVersion(_ value: String?) -> String? {
@@ -74,6 +74,9 @@ class Bridge {
74
74
  }
75
75
 
76
76
  static func log(_ message: String) {
77
+ // Native diagnostics and the JS console share this event. Do not capture
78
+ // stdout: React Native can write the forwarded message back to stdout.
79
+ NSLog("%@", message)
77
80
  let data = ["message": message]
78
81
  Bridge.sendTypedMessage("log", body: data)
79
82
  }
@@ -664,5 +667,3 @@ class Bridge {
664
667
  return payload
665
668
  }
666
669
  }
667
-
668
-
@@ -1823,7 +1823,7 @@ struct ViewState {
1823
1823
  sgc.requestPhoto(routed)
1824
1824
  }
1825
1825
 
1826
- func connectDefault() {
1826
+ func connectDefault(requiresAncs: Bool = true) {
1827
1827
  if defaultWearable.isEmpty {
1828
1828
  Bridge.log("MAN: No default wearable, returning")
1829
1829
  return
@@ -1839,6 +1839,7 @@ struct ViewState {
1839
1839
  return
1840
1840
  }
1841
1841
  initSGC(defaultWearable)
1842
+ (sgc as? MentraLive)?.setRequiresAncs(requiresAncs)
1842
1843
  if let live = sgc as? MentraLive, live.isPairingYieldActive() {
1843
1844
  Bridge.log("MAN: connectDefault skipped โ€” Mentra Live pairing yield active")
1844
1845
  return
@@ -1862,7 +1863,7 @@ struct ViewState {
1862
1863
  controller?.connectById(controllerDeviceName)
1863
1864
  }
1864
1865
 
1865
- func connectByName(_ dName: String) {
1866
+ func connectByName(_ dName: String, requiresAncs: Bool = true) {
1866
1867
  Bridge.log("MAN: Connecting to wearable: \(dName)")
1867
1868
  var name = dName
1868
1869
 
@@ -1895,6 +1896,7 @@ struct ViewState {
1895
1896
  self.pendingDeviceName = name
1896
1897
 
1897
1898
  initSGC(self.pendingWearable)
1899
+ (sgc as? MentraLive)?.setRequiresAncs(requiresAncs)
1898
1900
  sgc?.connectById(name)
1899
1901
  }
1900
1902
  }
@@ -3,9 +3,9 @@ import Foundation
3
3
  /// Generated by release CI. Do not edit in a release checkout.
4
4
  enum GeneratedReleaseMetadata {
5
5
  static let familyBaseVersion = "3.2.0"
6
- static let releaseIdentity = "3.2.0-dev.191"
7
- static let releaseSetId = "mentra-3.2.0-dev.191"
8
- static let sourceCommit = "3516939c2afd92634e7f3b3f1983f0671c8e1e27"
9
- static let otaManifestUrl = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.191.json"
10
- static let otaManifestSha256 = "95911977308516fa70d367a18af66f7075440aa219285ac23d5aa72164d475c4"
6
+ static let releaseIdentity = "3.2.0-dev.195"
7
+ static let releaseSetId = "mentra-3.2.0-dev.195"
8
+ static let sourceCommit = "321202ca96ec1852be19a7230ebcc3593fe87e54"
9
+ static let otaManifestUrl = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.195.json"
10
+ static let otaManifestSha256 = "70f26063b2c281b9b046965f6f6d7ca22f8b53242c6c5be02030c5a04c7115e4"
11
11
  }
@@ -191,6 +191,7 @@ public final class MentraBluetoothSDK {
191
191
  private var bluetoothAvailabilityListenerId: UUID?
192
192
  private var shouldRestoreGlassesOnBluetoothRestore = false
193
193
  private var shouldRestoreControllerOnBluetoothRestore = false
194
+ private var requiresAncsForBluetoothRestore = true
194
195
  private var bridgeEventSinkId: String?
195
196
  private var storeListenerId: String?
196
197
  private let defaultDeviceKeys: Set<String> = ["default_wearable", "device_name", "device_address", "project_name"]
@@ -408,6 +409,9 @@ public final class MentraBluetoothSDK {
408
409
  try BluetoothAvailability.shared.requirePoweredOn(operation: "connect to glasses")
409
410
  }
410
411
  let isController = ControllerTypes.ALL.contains(device.model.deviceType)
412
+ if !isController {
413
+ requiresAncsForBluetoothRestore = options.requiresAncs
414
+ }
411
415
  if options.cancelExistingConnectionAttempt {
412
416
  if isController {
413
417
  DeviceManager.shared.disconnectController()
@@ -427,11 +431,12 @@ public final class MentraBluetoothSDK {
427
431
  )
428
432
  }
429
433
  DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "pending_wearable", device.model.deviceType)
430
- DeviceManager.shared.connectByName(device.name)
434
+ DeviceManager.shared.connectByName(device.name, requiresAncs: options.requiresAncs)
431
435
  }
432
436
 
433
437
  public func connectDefault(options: ConnectOptions = ConnectOptions()) throws {
434
438
  clearBluetoothRestoreIntent()
439
+ requiresAncsForBluetoothRestore = options.requiresAncs
435
440
  guard let device = currentDefaultDevice() else {
436
441
  throw BluetoothSdkError(
437
442
  code: "default_device_missing",
@@ -444,7 +449,7 @@ public final class MentraBluetoothSDK {
444
449
  if options.cancelExistingConnectionAttempt {
445
450
  cancelConnectionAttempt()
446
451
  }
447
- DeviceManager.shared.connectDefault()
452
+ DeviceManager.shared.connectDefault(requiresAncs: options.requiresAncs)
448
453
  }
449
454
 
450
455
  public func cancelConnectionAttempt() {
@@ -1552,7 +1557,9 @@ public final class MentraBluetoothSDK {
1552
1557
  clearBluetoothRestoreIntent()
1553
1558
 
1554
1559
  if restoreGlasses, !glassesStatus.connected, glassesStatus.connectionState == .disconnected {
1555
- DeviceManager.shared.connectDefault() // also restores the controller
1560
+ DeviceManager.shared.connectDefault(
1561
+ requiresAncs: requiresAncsForBluetoothRestore
1562
+ ) // also restores the controller
1556
1563
  } else if restoreController, !glassesStatus.controllerConnected {
1557
1564
  DeviceManager.shared.connectDefaultController()
1558
1565
  }
@@ -84,6 +84,9 @@ enum BleTraceLogger {
84
84
 
85
85
  private static func emit(_ line: String) {
86
86
  os_log("%{public}@", log: log, type: .info, line)
87
+ // Log events are excluded from tracing in Bridge, so forwarding a trace
88
+ // cannot recursively generate another trace.
89
+ Bridge.sendTypedMessage("log", body: ["message": line])
87
90
  }
88
91
 
89
92
  private static func format(
@@ -1677,7 +1677,7 @@ extension G1 {
1677
1677
  Task {
1678
1678
  let success = await setBrightnessRaw(brightnessLevel, autoMode: autoMode)
1679
1679
  if !success {
1680
- NSLog("Failed to set brightness to level \(level)% (mapped to \(mappedLevel))")
1680
+ Bridge.log("Failed to set brightness to level \(level)% (mapped to \(mappedLevel))")
1681
1681
  }
1682
1682
  }
1683
1683
  }
@@ -497,7 +497,7 @@ class BlePhotoUploadService {
497
497
 
498
498
  request.httpBody = body
499
499
 
500
- print("LIVE: Uploading photo to webhook: \(webhookUrl)")
500
+ Bridge.log("LIVE: Uploading photo to webhook: \(webhookUrl)")
501
501
 
502
502
  do {
503
503
  let (data, response) = try await URLSession.shared.data(for: request)
@@ -513,7 +513,7 @@ class BlePhotoUploadService {
513
513
  )
514
514
  }
515
515
 
516
- print("LIVE: Upload successful. Response code: \(httpResponse.statusCode)")
516
+ Bridge.log("LIVE: Upload successful. Response code: \(httpResponse.statusCode)")
517
517
  return String(data: data, encoding: .utf8) ?? ""
518
518
 
519
519
  } catch {
@@ -627,7 +627,7 @@ enum K900ProtocolUtils {
627
627
  // Verify packet has enough data
628
628
  let requiredLength = pos + Int(info.packSize) + LENGTH_FILE_VERIFY + LENGTH_FILE_END
629
629
  if protocolData.count < requiredLength {
630
- print(
630
+ Bridge.log(
631
631
  "K900ProtocolUtils: File packet too short for data. Need: \(requiredLength), Have: \(protocolData.count), packSize=\(info.packSize), pos=\(pos)"
632
632
  )
633
633
  return nil
@@ -656,11 +656,11 @@ enum K900ProtocolUtils {
656
656
  info.isValid = (calculatedVerify == info.verifyCode)
657
657
 
658
658
  if !info.isValid {
659
- print(
659
+ Bridge.log(
660
660
  "K900ProtocolUtils: File packet checksum failed. Expected: \(String(format: "%02X", info.verifyCode)), Calculated: \(String(format: "%02X", calculatedVerify))"
661
661
  )
662
662
  } else if shouldLogFilePacket(info) {
663
- print(
663
+ Bridge.log(
664
664
  "K900ProtocolUtils: File packet extracted successfully: index=\(info.packIndex), size=\(info.packSize), fileName=\(info.fileName)"
665
665
  )
666
666
  }
@@ -732,7 +732,7 @@ private struct FileTransferSession {
732
732
  if isBesLie {
733
733
  // BES lie detected: totalPackets = fileSize / 400
734
734
  newTotalPackets = fileSize / Self.BES_HARDCODED_PACK_SIZE
735
- print(
735
+ Bridge.log(
736
736
  "๐Ÿ“ฆ BES Lie detected! fakeFileSize=\(fileSize), totalPackets=\(newTotalPackets), actualPackSize=\(actualPackSize)"
737
737
  )
738
738
  } else {
@@ -741,7 +741,7 @@ private struct FileTransferSession {
741
741
  }
742
742
 
743
743
  if newTotalPackets != totalPackets {
744
- print(
744
+ Bridge.log(
745
745
  "๐Ÿ“ฆ Recalculating totalPackets: \(totalPackets) -> \(newTotalPackets) (packSize=\(actualPackSize), fileSize=\(fileSize))"
746
746
  )
747
747
  totalPackets = newTotalPackets
@@ -793,7 +793,7 @@ private struct FileTransferSession {
793
793
  // Calculate actual file size by summing all received packet sizes
794
794
  let actualFileSize = receivedPackets.values.reduce(0) { $0 + $1.count }
795
795
 
796
- print(
796
+ Bridge.log(
797
797
  "๐Ÿ“ฆ Assembling file: headerFileSize=\(fileSize), actualFileSize=\(actualFileSize), totalPackets=\(totalPackets)"
798
798
  )
799
799
 
@@ -1384,6 +1384,17 @@ enum MentraLiveConnectionState {
1384
1384
  case connected
1385
1385
  }
1386
1386
 
1387
+ enum MentraLiveConnectionOptions {
1388
+ static func coreBluetoothOptions(requiresAncs: Bool) -> [String: Any]? {
1389
+ #if os(macOS)
1390
+ return nil
1391
+ #else
1392
+ guard requiresAncs else { return nil }
1393
+ return [CBConnectPeripheralOptionRequiresANCS: true]
1394
+ #endif
1395
+ }
1396
+ }
1397
+
1387
1398
  /// Type aliases for compatibility
1388
1399
  typealias JSONObject = [String: Any]
1389
1400
 
@@ -1743,6 +1754,7 @@ class MentraLive: NSObject, SGCManager {
1743
1754
 
1744
1755
  private var connectionTimeoutTimer: Timer?
1745
1756
  private var reconnectionWorkItem: DispatchWorkItem?
1757
+ private var requiresAncs = true
1746
1758
 
1747
1759
  // MARK: - Initialization
1748
1760
 
@@ -1775,6 +1787,10 @@ class MentraLive: NSObject, SGCManager {
1775
1787
  destroy()
1776
1788
  }
1777
1789
 
1790
+ func setRequiresAncs(_ requiresAncs: Bool) {
1791
+ self.requiresAncs = requiresAncs
1792
+ }
1793
+
1778
1794
  // MARK: - React Native Interface
1779
1795
 
1780
1796
  private var discoveredPeripherals = [String: CBPeripheral]() // name -> peripheral
@@ -2448,11 +2464,12 @@ class MentraLive: NSObject, SGCManager {
2448
2464
  centralManager?.connect(peripheral, options: nil)
2449
2465
  #else
2450
2466
  // ANCS is hosted by iOS and is only exposed to authorized accessories.
2451
- // Requiring it at connect time lets the system complete that authorization
2452
- // flow before the glasses subscribe to the ANCS characteristics.
2467
+ // The default requirement lets the system complete that authorization flow
2468
+ // before the glasses subscribe; apps that do not relay notifications can opt out.
2469
+ Bridge.log("LIVE: ANCS connection requirement \(requiresAncs ? "enabled" : "disabled")")
2453
2470
  centralManager?.connect(
2454
2471
  peripheral,
2455
- options: [CBConnectPeripheralOptionRequiresANCS: true]
2472
+ options: MentraLiveConnectionOptions.coreBluetoothOptions(requiresAncs: requiresAncs)
2456
2473
  )
2457
2474
  #endif
2458
2475
  }
@@ -143,7 +143,7 @@ class STTTools {
143
143
  )
144
144
 
145
145
  if !success || extractionError != nil {
146
- print(
146
+ Bridge.log(
147
147
  "EXTRACTION_ERROR: \(extractionError?.localizedDescription ?? "Failed to extract tar.bz2")"
148
148
  )
149
149
  return false
@@ -169,9 +169,17 @@ public struct Device: Identifiable, Equatable, CustomStringConvertible {
169
169
  public struct ConnectOptions {
170
170
  public let saveAsDefault: Bool
171
171
  public let cancelExistingConnectionAttempt: Bool
172
+ /// Mentra Live on iOS only. Whether CoreBluetooth should require ANCS
173
+ /// authorization while establishing the connection.
174
+ public let requiresAncs: Bool
172
175
 
173
- public init(saveAsDefault: Bool = true, cancelExistingConnectionAttempt: Bool = true) {
176
+ public init(
177
+ saveAsDefault: Bool = true,
178
+ cancelExistingConnectionAttempt: Bool = true,
179
+ requiresAncs: Bool = true
180
+ ) {
174
181
  self.saveAsDefault = saveAsDefault
175
182
  self.cancelExistingConnectionAttempt = cancelExistingConnectionAttempt
183
+ self.requiresAncs = requiresAncs
176
184
  }
177
185
  }
@@ -2077,7 +2077,7 @@ class G1FontLoader {
2077
2077
  fontMap[character] = FontGlyph(width: width, height: height)
2078
2078
  }
2079
2079
 
2080
- print("Hardcoded font data loaded successfully! \(fontMap.count) glyphs mapped.")
2080
+ Bridge.log("Hardcoded font data loaded successfully! \(fontMap.count) glyphs mapped.")
2081
2081
  }
2082
2082
 
2083
2083
  func getGlyph(_ character: Character) -> FontGlyph {
@@ -62,8 +62,7 @@ private func jlog(_ message: String) {
62
62
  /// is fully booted (RN bridge up, Metro pull done if dev, etc).
63
63
  @objc static func maybeAutoBenchmark() {
64
64
  guard ProcessInfo.processInfo.environment["MENTRA_RUN_JSC_BENCH"] != nil else { return }
65
- os_log("๐Ÿงช MENTRA_RUN_JSC_BENCH set โ€” auto-running benchmark in 5s",
66
- log: jscLog, type: .info)
65
+ jlog("๐Ÿงช MENTRA_RUN_JSC_BENCH set โ€” auto-running benchmark in 5s")
67
66
  DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 5.0) {
68
67
  runBenchmark()
69
68
  }
@@ -12,7 +12,7 @@ final class MessageChunkReassembler {
12
12
  cleanupTimedOutSessions()
13
13
 
14
14
  guard fragCount > 0, fragIdx >= 0, fragIdx < fragCount else {
15
- print("MessageChunkReassembler: Dropping invalid binary fragment for msgId \(msgId)")
15
+ Bridge.log("MessageChunkReassembler: Dropping invalid binary fragment for msgId \(msgId)")
16
16
  return nil
17
17
  }
18
18
 
@@ -42,7 +42,7 @@ final class MessageChunkReassembler {
42
42
 
43
43
  let reassembled = session.reassemble()
44
44
  activeBinarySessions.removeValue(forKey: msgId)
45
- print("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(fragCount) binary fragments")
45
+ Bridge.log("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(fragCount) binary fragments")
46
46
  return reassembled
47
47
  }
48
48
 
@@ -54,12 +54,12 @@ final class MessageChunkReassembler {
54
54
  info.chunkIndex >= 0,
55
55
  info.chunkIndex < info.totalChunks
56
56
  else {
57
- print("MessageChunkReassembler: Dropping invalid chunk metadata for \(info.chunkId)")
57
+ Bridge.log("MessageChunkReassembler: Dropping invalid chunk metadata for \(info.chunkId)")
58
58
  return nil
59
59
  }
60
60
 
61
61
  if let existing = activeSessions[info.chunkId], existing.totalChunks != info.totalChunks {
62
- print(
62
+ Bridge.log(
63
63
  "MessageChunkReassembler: totalChunks mismatch for \(info.chunkId) (expected \(existing.totalChunks), got \(info.totalChunks)); resetting session"
64
64
  )
65
65
  activeSessions.removeValue(forKey: info.chunkId)
@@ -78,7 +78,7 @@ final class MessageChunkReassembler {
78
78
  )
79
79
 
80
80
  guard session.addChunk(index: info.chunkIndex, data: info.data) else {
81
- print("MessageChunkReassembler: Failed to add chunk \(info.chunkIndex) for \(info.chunkId)")
81
+ Bridge.log("MessageChunkReassembler: Failed to add chunk \(info.chunkIndex) for \(info.chunkId)")
82
82
  return nil
83
83
  }
84
84
  if isNewSession {
@@ -91,7 +91,7 @@ final class MessageChunkReassembler {
91
91
 
92
92
  let reassembled = session.reassemble()
93
93
  activeSessions.removeValue(forKey: info.chunkId)
94
- print("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(info.totalChunks) chunks")
94
+ Bridge.log("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(info.totalChunks) chunks")
95
95
  return reassembled
96
96
  }
97
97