@mentra/bluetooth-sdk 0.1.3 → 0.1.5

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 (61) hide show
  1. package/README.md +63 -18
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +5 -5
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +38 -12
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +1 -1
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +88 -3
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +11 -5
  7. package/app.plugin.js +3 -1
  8. package/build/BluetoothSdk.types.d.ts +20 -7
  9. package/build/BluetoothSdk.types.d.ts.map +1 -1
  10. package/build/BluetoothSdk.types.js.map +1 -1
  11. package/build/_internal.d.ts +1 -1
  12. package/build/_internal.js +1 -1
  13. package/build/_internal.js.map +1 -1
  14. package/build/_private/BluetoothSdkModule.d.ts +5 -4
  15. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  16. package/build/_private/BluetoothSdkModule.js +1 -1
  17. package/build/_private/BluetoothSdkModule.js.map +1 -1
  18. package/build/index.d.ts +4 -3
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +81 -2
  21. package/build/index.js.map +1 -1
  22. package/build/react/index.d.ts +5 -0
  23. package/build/react/index.d.ts.map +1 -0
  24. package/build/react/index.js +4 -0
  25. package/build/react/index.js.map +1 -0
  26. package/build/react/useBluetoothEvent.d.ts +6 -0
  27. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  28. package/build/react/useBluetoothEvent.js +21 -0
  29. package/build/react/useBluetoothEvent.js.map +1 -0
  30. package/build/react/useBluetoothScan.d.ts +22 -0
  31. package/build/react/useBluetoothScan.d.ts.map +1 -0
  32. package/build/react/useBluetoothScan.js +135 -0
  33. package/build/react/useBluetoothScan.js.map +1 -0
  34. package/build/react/useBluetoothStatus.d.ts +16 -0
  35. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  36. package/build/react/useBluetoothStatus.js +137 -0
  37. package/build/react/useBluetoothStatus.js.map +1 -0
  38. package/build/react/useGlassesConnection.d.ts +29 -0
  39. package/build/react/useGlassesConnection.d.ts.map +1 -0
  40. package/build/react/useGlassesConnection.js +153 -0
  41. package/build/react/useGlassesConnection.js.map +1 -0
  42. package/build/react/useMentraBluetooth.d.ts +98 -0
  43. package/build/react/useMentraBluetooth.d.ts.map +1 -0
  44. package/build/react/useMentraBluetooth.js +156 -0
  45. package/build/react/useMentraBluetooth.js.map +1 -0
  46. package/ios/BluetoothSdkModule.swift +5 -5
  47. package/ios/Source/Bridge.swift +42 -8
  48. package/ios/Source/DeviceManager.swift +0 -1
  49. package/ios/Source/DeviceStore.swift +1 -1
  50. package/ios/Source/MentraBluetoothSDK.swift +117 -10
  51. package/package.json +7 -1
  52. package/src/BluetoothSdk.types.ts +20 -7
  53. package/src/_internal.ts +1 -1
  54. package/src/_private/BluetoothSdkModule.ts +7 -5
  55. package/src/index.ts +93 -4
  56. package/src/react/index.ts +24 -0
  57. package/src/react/useBluetoothEvent.ts +38 -0
  58. package/src/react/useBluetoothScan.ts +173 -0
  59. package/src/react/useBluetoothStatus.ts +180 -0
  60. package/src/react/useGlassesConnection.ts +209 -0
  61. package/src/react/useMentraBluetooth.ts +307 -0
@@ -0,0 +1,156 @@
1
+ import { useState } from "react";
2
+ import BluetoothSdk from "../index";
3
+ import { DeviceModels, createDisconnectedGlassesStatus, isConnectedGlassesConnectionStatus, isReadyGlassesConnectionStatus, } from "../BluetoothSdk.types";
4
+ import { useGlassesConnection, } from "./useGlassesConnection";
5
+ function stringValue(value) {
6
+ return typeof value === "string" && value.length > 0 ? value : undefined;
7
+ }
8
+ function numberValue(value) {
9
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
10
+ }
11
+ function firmwareInfo(status) {
12
+ const firmwareSources = [
13
+ ["firmwareVersion", "firmware"],
14
+ ["deviceFirmwareVersion", "device"],
15
+ ["rightFirmwareVersion", "right"],
16
+ ["leftFirmwareVersion", "left"],
17
+ ["besFirmwareVersion", "bes"],
18
+ ["mtkFirmwareVersion", "mtk"],
19
+ ["appVersion", "app"],
20
+ ];
21
+ for (const [key, source] of firmwareSources) {
22
+ const value = stringValue(status[key]);
23
+ if (value) {
24
+ return {
25
+ appVersion: stringValue(status.appVersion),
26
+ buildNumber: stringValue(status.buildNumber),
27
+ source,
28
+ version: value,
29
+ };
30
+ }
31
+ }
32
+ return {
33
+ appVersion: stringValue(status.appVersion),
34
+ buildNumber: stringValue(status.buildNumber),
35
+ source: "unknown",
36
+ version: null,
37
+ };
38
+ }
39
+ function batteryState(status) {
40
+ const level = typeof status.batteryLevel === "number" && status.batteryLevel >= 0 ? status.batteryLevel : null;
41
+ return {
42
+ charging: status.charging ?? false,
43
+ level,
44
+ };
45
+ }
46
+ function connectedGlassesInfo(status) {
47
+ return {
48
+ appVersion: stringValue(status.appVersion),
49
+ bluetoothName: stringValue(status.bluetoothName),
50
+ buildNumber: stringValue(status.buildNumber),
51
+ color: stringValue(status.color),
52
+ deviceModel: stringValue(status.deviceModel),
53
+ firmwareVersion: stringValue(status.firmwareVersion),
54
+ serialNumber: stringValue(status.serialNumber),
55
+ style: stringValue(status.style),
56
+ };
57
+ }
58
+ function runtimeGlassesState(status) {
59
+ const connection = status.connection ?? createDisconnectedGlassesStatus().connection ?? { state: "disconnected" };
60
+ if (!isConnectedGlassesConnectionStatus(connection)) {
61
+ return {
62
+ connected: false,
63
+ connection,
64
+ ready: false,
65
+ };
66
+ }
67
+ return {
68
+ battery: batteryState(status),
69
+ connected: true,
70
+ connection,
71
+ device: connectedGlassesInfo(status),
72
+ firmware: firmwareInfo(status),
73
+ hotspot: status.hotspot ?? { state: "disabled" },
74
+ ready: isReadyGlassesConnectionStatus(connection),
75
+ signal: {
76
+ strengthDbm: numberValue(status.signalStrength),
77
+ updatedAt: numberValue(status.signalStrengthUpdatedAt),
78
+ },
79
+ wifi: status.wifi ?? { state: "disconnected" },
80
+ };
81
+ }
82
+ function phoneSdkState(status, defaultDevice, galleryMode) {
83
+ return {
84
+ currentMic: status.currentMic || null,
85
+ defaultDevice,
86
+ galleryMode,
87
+ lastLog: status.lastLog ?? [],
88
+ micRanking: status.micRanking ?? [],
89
+ otherBluetoothConnected: status.otherBtConnected ?? false,
90
+ searching: status.searching ?? false,
91
+ searchingController: status.searchingController ?? false,
92
+ systemMicUnavailable: status.systemMicUnavailable ?? false,
93
+ wifiScanResults: status.wifiScanResults ?? [],
94
+ };
95
+ }
96
+ function scanController(connection) {
97
+ return {
98
+ active: connection.scan.scanning,
99
+ clear: connection.scan.clearResults,
100
+ devices: connection.scan.devices,
101
+ error: connection.scan.error,
102
+ model: connection.scan.model,
103
+ selectedDevice: connection.scan.selectedDevice,
104
+ selectDevice: connection.scan.selectDevice,
105
+ setModel: connection.scan.setModel,
106
+ start: connection.scan.startScan,
107
+ stop: connection.scan.stopScan,
108
+ };
109
+ }
110
+ export function useMentraBluetooth(options = {}) {
111
+ const connection = useGlassesConnection({
112
+ autoConnectDefault: options.autoConnectDefault,
113
+ defaultDeviceStorage: options.defaultDeviceStorage,
114
+ onError: options.onError,
115
+ scanModel: options.defaultModel ?? DeviceModels.MentraLive,
116
+ scanTimeoutMs: options.scanTimeoutMs,
117
+ });
118
+ const [galleryModeApplying, setGalleryModeApplying] = useState(false);
119
+ const [galleryModeError, setGalleryModeError] = useState(null);
120
+ async function setGalleryMode(mode) {
121
+ setGalleryModeApplying(true);
122
+ setGalleryModeError(null);
123
+ try {
124
+ await BluetoothSdk.setGalleryMode(mode);
125
+ }
126
+ catch (error) {
127
+ setGalleryModeError(error);
128
+ options.onError?.(error);
129
+ throw error;
130
+ }
131
+ finally {
132
+ setGalleryModeApplying(false);
133
+ }
134
+ }
135
+ const galleryMode = {
136
+ applying: galleryModeApplying,
137
+ desired: connection.bluetoothStatus.galleryModeAuto === false ? "manual" : "auto",
138
+ error: galleryModeError,
139
+ };
140
+ return {
141
+ busy: connection.busy || galleryModeApplying,
142
+ clearDefaultDevice: connection.clearDefaultDevice,
143
+ connect: connection.connect,
144
+ connectDefault: connection.connectDefault,
145
+ defaultDevice: connection.defaultDevice,
146
+ disconnect: connection.disconnect,
147
+ error: galleryModeError ?? connection.error,
148
+ glasses: runtimeGlassesState(connection.glassesStatus),
149
+ refresh: connection.refresh,
150
+ scan: scanController(connection),
151
+ sdk: phoneSdkState(connection.bluetoothStatus, connection.defaultDevice, galleryMode),
152
+ setDefaultDevice: connection.setDefaultDevice,
153
+ setGalleryMode,
154
+ };
155
+ }
156
+ //# sourceMappingURL=useMentraBluetooth.js.map
@@ -0,0 +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 DeviceModels,\n createDisconnectedGlassesStatus,\n isConnectedGlassesConnectionStatus,\n isReadyGlassesConnectionStatus,\n} from \"../BluetoothSdk.types\"\nimport type {\n ConnectOptions,\n Device,\n DeviceModel,\n GalleryMode,\n GlassesConnectionStatus,\n HotspotStatus,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n MicMode,\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 wifi: WifiStatus\n }\n\nexport type GalleryModeState = {\n applying: boolean\n desired: GalleryMode\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 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 setGalleryMode: (mode: GalleryMode) => 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 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 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 setGalleryMode(mode: GalleryMode) {\n setGalleryModeApplying(true)\n setGalleryModeError(null)\n try {\n await BluetoothSdk.setGalleryMode(mode)\n } catch (error) {\n setGalleryModeError(error)\n options.onError?.(error)\n throw error\n } finally {\n setGalleryModeApplying(false)\n }\n }\n\n const galleryMode: GalleryModeState = {\n applying: galleryModeApplying,\n desired: connection.bluetoothStatus.galleryModeAuto === false ? \"manual\" : \"auto\",\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 setGalleryMode,\n }\n}\n"]}
@@ -457,7 +457,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
457
457
  self.bluetoothSdk().setMicState(
458
458
  enabled: enabled,
459
459
  useGlassesMic: useGlassesMic ?? true,
460
- bypassVad: bypassVad ?? false,
460
+ bypassVad: bypassVad ?? true,
461
461
  sendTranscript: sendTranscript ?? false,
462
462
  sendLc3Data: sendLc3Data ?? false
463
463
  )
@@ -589,13 +589,13 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
589
589
  }
590
590
 
591
591
  @MainActor
592
- public func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicPcm frame: Data) {
593
- sendEvent("mic_pcm", ["pcm": frame])
592
+ public func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicPcm event: MicPcmEvent) {
593
+ sendEvent("mic_pcm", event.values)
594
594
  }
595
595
 
596
596
  @MainActor
597
- public func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 frame: Data) {
598
- sendEvent("mic_lc3", ["lc3": frame])
597
+ public func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 event: MicLc3Event) {
598
+ sendEvent("mic_lc3", event.values)
599
599
  }
600
600
 
601
601
  @MainActor
@@ -10,6 +10,11 @@ import Foundation
10
10
  /// Bridge for Bluetooth SDK communication between Expo modules and native iOS code
11
11
  /// Has commands for the Bluetooth SDK to use to send messages to JavaScript
12
12
  class Bridge {
13
+ private static let micSampleRate = 16_000
14
+ private static let pcmBitsPerSample = 16
15
+ private static let micChannels = 1
16
+ private static let lc3FrameDurationMs = 10
17
+ private static let defaultLc3FrameSizeBytes = 60
13
18
  private static let eventSinkLock = NSLock()
14
19
  private static let defaultEventSinkId = "default"
15
20
  private static var eventSinks: [String: (String, [String: Any]) -> Void] = [:]
@@ -72,18 +77,47 @@ class Bridge {
72
77
  Bridge.sendTypedMessage("pair_failure", body: data)
73
78
  }
74
79
 
80
+ @MainActor
75
81
  static func sendMicPcm(_ data: Data) {
76
- // let base64String = data.base64EncodedString()
77
- // let body = ["base64": base64String]
78
- let body = ["pcm": data]
79
- Bridge.sendTypedMessage("mic_pcm", body: body)
82
+ Bridge.sendTypedMessage("mic_pcm", body: micPcmEventBody(data))
80
83
  }
81
84
 
85
+ @MainActor
82
86
  static func sendMicLc3(_ data: Data) {
83
- // let base64String = data.base64EncodedString()
84
- // let body = ["base64": base64String]
85
- let body = ["lc3": data]
86
- Bridge.sendTypedMessage("mic_lc3", body: body)
87
+ Bridge.sendTypedMessage("mic_lc3", body: micLc3EventBody(data))
88
+ }
89
+
90
+ @MainActor
91
+ private static func micPcmEventBody(_ data: Data) -> [String: Any] {
92
+ [
93
+ "pcm": data,
94
+ "sampleRate": micSampleRate,
95
+ "bitsPerSample": pcmBitsPerSample,
96
+ "channels": micChannels,
97
+ "encoding": "pcm_s16le",
98
+ "vadGated": isVadGated(),
99
+ ]
100
+ }
101
+
102
+ @MainActor
103
+ private static func micLc3EventBody(_ data: Data) -> [String: Any] {
104
+ let frameSizeBytes = DeviceStore.shared.get("bluetooth", "lc3_frame_size") as? Int ?? defaultLc3FrameSizeBytes
105
+ return [
106
+ "lc3": data,
107
+ "sampleRate": micSampleRate,
108
+ "channels": micChannels,
109
+ "encoding": "lc3",
110
+ "frameDurationMs": lc3FrameDurationMs,
111
+ "frameSizeBytes": frameSizeBytes,
112
+ "bitrate": frameSizeBytes * 8 * (1000 / lc3FrameDurationMs),
113
+ "packetizedFromGlasses": false,
114
+ "vadGated": isVadGated(),
115
+ ]
116
+ }
117
+
118
+ @MainActor
119
+ private static func isVadGated() -> Bool {
120
+ !(DeviceStore.shared.get("bluetooth", "bypass_vad") as? Bool ?? true)
87
121
  }
88
122
 
89
123
  static func saveSetting(_ key: String, _ value: Any) {
@@ -1013,7 +1013,6 @@ struct ViewState {
1013
1013
 
1014
1014
  func handleDeviceDisconnected() {
1015
1015
  Bridge.log("MAN: Device disconnected")
1016
- // setMicState(shouldSendPcData, shouldSendTranscript, false)
1017
1016
  // shouldSendBootingMessage = true // Reset for next first connect
1018
1017
  }
1019
1018
 
@@ -96,7 +96,7 @@ class DeviceStore {
96
96
  store.set("bluetooth", "should_send_pcm", false)
97
97
  store.set("bluetooth", "should_send_lc3", false)
98
98
  store.set("bluetooth", "should_send_transcript", false)
99
- store.set("bluetooth", "bypass_vad", false)
99
+ store.set("bluetooth", "bypass_vad", true)
100
100
  }
101
101
 
102
102
  func get(_ category: String, _ key: String) -> Any? {
@@ -159,8 +159,10 @@ public enum DeviceModel: String {
159
159
  public struct Device: Identifiable, Equatable, CustomStringConvertible {
160
160
  public let model: DeviceModel
161
161
  public let name: String
162
+ /// CoreBluetooth identifier when available.
162
163
  public let identifier: String?
163
164
  public let rssi: Int?
165
+ /// Stable app-facing scan-result key. Do not parse; use typed fields instead.
164
166
  public let id: String
165
167
 
166
168
  public init(
@@ -527,7 +529,7 @@ public struct BluetoothStatus: CustomStringConvertible {
527
529
  public var shouldSendPcm: Bool { boolValue(values, "should_send_pcm") ?? false }
528
530
  public var shouldSendLc3: Bool { boolValue(values, "should_send_lc3") ?? false }
529
531
  public var shouldSendTranscript: Bool { boolValue(values, "should_send_transcript") ?? false }
530
- public var bypassVad: Bool { boolValue(values, "bypass_vad") ?? false }
532
+ public var bypassVad: Bool { boolValue(values, "bypass_vad") ?? true }
531
533
  public var offlineCaptionsRunning: Bool { boolValue(values, "offline_captions_running") ?? false }
532
534
  public var localSttFallbackActive: Bool { boolValue(values, "local_stt_fallback_active") ?? false }
533
535
  public var shouldSendBootingMessage: Bool { boolValue(values, "shouldSendBootingMessage") ?? true }
@@ -1912,6 +1914,109 @@ public struct LocalTranscriptionEvent: CustomStringConvertible {
1912
1914
  }
1913
1915
  }
1914
1916
 
1917
+ public struct MicPcmEvent: CustomStringConvertible {
1918
+ public static let sampleRate = 16_000
1919
+ public static let bitsPerSample = 16
1920
+ public static let channels = 1
1921
+ public static let encoding = "pcm_s16le"
1922
+
1923
+ public let pcm: Data
1924
+ public let sampleRate: Int
1925
+ public let bitsPerSample: Int
1926
+ public let channels: Int
1927
+ public let encoding: String
1928
+ public let vadGated: Bool
1929
+ public let values: [String: Any]
1930
+
1931
+ public init(values: [String: Any]) {
1932
+ let pcm = values["pcm"] as? Data ?? Data()
1933
+ let sampleRate = intValue(values["sampleRate"]) ?? Self.sampleRate
1934
+ let bitsPerSample = intValue(values["bitsPerSample"]) ?? Self.bitsPerSample
1935
+ let channels = intValue(values["channels"]) ?? Self.channels
1936
+ let encoding = values["encoding"] as? String ?? Self.encoding
1937
+ let vadGated = boolValue(values, "vadGated") ?? false
1938
+
1939
+ var normalized = values
1940
+ normalized["type"] = "mic_pcm"
1941
+ normalized["pcm"] = pcm
1942
+ normalized["sampleRate"] = sampleRate
1943
+ normalized["bitsPerSample"] = bitsPerSample
1944
+ normalized["channels"] = channels
1945
+ normalized["encoding"] = encoding
1946
+ normalized["vadGated"] = vadGated
1947
+
1948
+ self.pcm = pcm
1949
+ self.sampleRate = sampleRate
1950
+ self.bitsPerSample = bitsPerSample
1951
+ self.channels = channels
1952
+ self.encoding = encoding
1953
+ self.vadGated = vadGated
1954
+ self.values = normalized
1955
+ }
1956
+
1957
+ public var description: String {
1958
+ "MicPcmEvent(bytes: \(pcm.count), sampleRate: \(sampleRate), bitsPerSample: \(bitsPerSample), channels: \(channels), encoding: \(encoding), vadGated: \(vadGated))"
1959
+ }
1960
+ }
1961
+
1962
+ public struct MicLc3Event: CustomStringConvertible {
1963
+ public static let sampleRate = 16_000
1964
+ public static let channels = 1
1965
+ public static let encoding = "lc3"
1966
+ public static let frameDurationMs = 10
1967
+ public static let defaultFrameSizeBytes = 60
1968
+
1969
+ public let lc3: Data
1970
+ public let sampleRate: Int
1971
+ public let channels: Int
1972
+ public let encoding: String
1973
+ public let frameDurationMs: Int
1974
+ public let frameSizeBytes: Int
1975
+ public let bitrate: Int
1976
+ public let packetizedFromGlasses: Bool
1977
+ public let vadGated: Bool
1978
+ public let values: [String: Any]
1979
+
1980
+ public init(values: [String: Any]) {
1981
+ let lc3 = values["lc3"] as? Data ?? Data()
1982
+ let sampleRate = intValue(values["sampleRate"]) ?? Self.sampleRate
1983
+ let channels = intValue(values["channels"]) ?? Self.channels
1984
+ let encoding = values["encoding"] as? String ?? Self.encoding
1985
+ let frameDurationMs = intValue(values["frameDurationMs"]) ?? Self.frameDurationMs
1986
+ let frameSizeBytes = intValue(values["frameSizeBytes"]) ?? Self.defaultFrameSizeBytes
1987
+ let bitrate = intValue(values["bitrate"]) ?? frameSizeBytes * 8 * (1000 / frameDurationMs)
1988
+ let packetizedFromGlasses = boolValue(values, "packetizedFromGlasses") ?? false
1989
+ let vadGated = boolValue(values, "vadGated") ?? false
1990
+
1991
+ var normalized = values
1992
+ normalized["type"] = "mic_lc3"
1993
+ normalized["lc3"] = lc3
1994
+ normalized["sampleRate"] = sampleRate
1995
+ normalized["channels"] = channels
1996
+ normalized["encoding"] = encoding
1997
+ normalized["frameDurationMs"] = frameDurationMs
1998
+ normalized["frameSizeBytes"] = frameSizeBytes
1999
+ normalized["bitrate"] = bitrate
2000
+ normalized["packetizedFromGlasses"] = packetizedFromGlasses
2001
+ normalized["vadGated"] = vadGated
2002
+
2003
+ self.lc3 = lc3
2004
+ self.sampleRate = sampleRate
2005
+ self.channels = channels
2006
+ self.encoding = encoding
2007
+ self.frameDurationMs = frameDurationMs
2008
+ self.frameSizeBytes = frameSizeBytes
2009
+ self.bitrate = bitrate
2010
+ self.packetizedFromGlasses = packetizedFromGlasses
2011
+ self.vadGated = vadGated
2012
+ self.values = normalized
2013
+ }
2014
+
2015
+ public var description: String {
2016
+ "MicLc3Event(bytes: \(lc3.count), sampleRate: \(sampleRate), channels: \(channels), frameDurationMs: \(frameDurationMs), frameSizeBytes: \(frameSizeBytes), bitrate: \(bitrate), packetizedFromGlasses: \(packetizedFromGlasses), vadGated: \(vadGated))"
2017
+ }
2018
+ }
2019
+
1915
2020
  public struct GlassesMediaVolumeGetResult: CustomStringConvertible {
1916
2021
  public let level: Int?
1917
2022
  public let statusCode: Int?
@@ -1989,8 +2094,8 @@ public protocol MentraBluetoothSDKDelegate: AnyObject {
1989
2094
  func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didDiscover device: Device)
1990
2095
  func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didStopScan reason: ScanStopReason)
1991
2096
  func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceive event: BluetoothEvent)
1992
- func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicPcm frame: Data)
1993
- func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicLc3 frame: Data)
2097
+ func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicPcm event: MicPcmEvent)
2098
+ func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicLc3 event: MicLc3Event)
1994
2099
  func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didChangeDefaultDevice device: Device?)
1995
2100
  func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didLog message: String)
1996
2101
  func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didFail error: BluetoothError)
@@ -2003,8 +2108,8 @@ public extension MentraBluetoothSDKDelegate {
2003
2108
  func mentraBluetoothSDK(_: MentraBluetoothSDK, didDiscover _: Device) {}
2004
2109
  func mentraBluetoothSDK(_: MentraBluetoothSDK, didStopScan _: ScanStopReason) {}
2005
2110
  func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceive _: BluetoothEvent) {}
2006
- func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicPcm _: Data) {}
2007
- func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 _: Data) {}
2111
+ func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicPcm _: MicPcmEvent) {}
2112
+ func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 _: MicLc3Event) {}
2008
2113
  func mentraBluetoothSDK(_: MentraBluetoothSDK, didChangeDefaultDevice _: Device?) {}
2009
2114
  func mentraBluetoothSDK(_: MentraBluetoothSDK, didLog _: String) {}
2010
2115
  func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail _: BluetoothError) {}
@@ -2298,7 +2403,7 @@ public final class MentraBluetoothSDK {
2298
2403
  public func setMicState(
2299
2404
  enabled: Bool,
2300
2405
  useGlassesMic: Bool = true,
2301
- bypassVad: Bool = false,
2406
+ bypassVad: Bool = true,
2302
2407
  sendTranscript: Bool = false,
2303
2408
  sendLc3Data: Bool = false
2304
2409
  ) {
@@ -2567,12 +2672,14 @@ public final class MentraBluetoothSDK {
2567
2672
  case "touch_event":
2568
2673
  delegate?.mentraBluetoothSDK(self, didReceive: .touch(TouchEvent(values: data)))
2569
2674
  case "mic_pcm":
2570
- if let frame = data["pcm"] as? Data {
2571
- delegate?.mentraBluetoothSDK(self, didReceiveMicPcm: frame)
2675
+ let event = MicPcmEvent(values: data)
2676
+ if !event.pcm.isEmpty {
2677
+ delegate?.mentraBluetoothSDK(self, didReceiveMicPcm: event)
2572
2678
  }
2573
2679
  case "mic_lc3":
2574
- if let frame = data["lc3"] as? Data {
2575
- delegate?.mentraBluetoothSDK(self, didReceiveMicLc3: frame)
2680
+ let event = MicLc3Event(values: data)
2681
+ if !event.lc3.isEmpty {
2682
+ delegate?.mentraBluetoothSDK(self, didReceiveMicLc3: event)
2576
2683
  }
2577
2684
  case "local_transcription":
2578
2685
  let event = LocalTranscriptionEvent(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/bluetooth-sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "SDK for communicating with smart glasses",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -12,10 +12,16 @@
12
12
  "react-native": "./src/index.ts",
13
13
  "default": "./build/index.js"
14
14
  },
15
+ "./react": {
16
+ "types": "./build/react/index.d.ts",
17
+ "react-native": "./src/react/index.ts",
18
+ "default": "./build/react/index.js"
19
+ },
15
20
  "./_private/*": null,
16
21
  "./build/*": null,
17
22
  "./src/*": null,
18
23
  "./app.plugin": "./app.plugin.js",
24
+ "./app.plugin.js": "./app.plugin.js",
19
25
  "./package.json": "./package.json"
20
26
  },
21
27
  "files": [
@@ -295,11 +295,24 @@ export type WsBinEvent = {
295
295
  export type MicPcmEvent = {
296
296
  type: "mic_pcm"
297
297
  pcm: ArrayBuffer
298
+ sampleRate: 16000
299
+ bitsPerSample: 16
300
+ channels: 1
301
+ encoding: "pcm_s16le"
302
+ vadGated: boolean
298
303
  }
299
304
 
300
305
  export type MicLc3Event = {
301
306
  type: "mic_lc3"
302
307
  lc3: ArrayBuffer
308
+ sampleRate: 16000
309
+ channels: 1
310
+ encoding: "lc3"
311
+ frameDurationMs: 10
312
+ frameSizeBytes: number
313
+ bitrate: number
314
+ packetizedFromGlasses: boolean
315
+ vadGated: boolean
303
316
  }
304
317
 
305
318
  export type StreamStatusLifecycleState = "initializing" | "streaming" | "stopping" | "stopped"
@@ -381,7 +394,7 @@ export type OtaUpdateAvailableEvent = {
381
394
  cache_ready?: boolean
382
395
  }
383
396
 
384
- /** @deprecated Glasses no longer emit ota_progress; use {@link OtaStatusEvent} and legacy store mapping. */
397
+ /** @deprecated Glasses no longer emit ota_progress; use {@link OtaStatusEvent} and status-store mapping. */
385
398
  export type OtaProgressEvent = {
386
399
  type: "ota_progress"
387
400
  stage?: OtaStage
@@ -491,8 +504,6 @@ export type PublicBluetoothStatus = Pick<
491
504
  >
492
505
 
493
506
  export type BluetoothSdkEventMap = {
494
- glasses_status: Partial<PublicGlassesStatus>
495
- bluetooth_status: Partial<PublicBluetoothStatus>
496
507
  log: LogEvent
497
508
  device_discovered: Device
498
509
  default_device_changed: {device?: Device}
@@ -540,8 +551,6 @@ export interface BluetoothSdkPublicModule {
540
551
  listener: BluetoothSdkEventListener<EventName>,
541
552
  ): BluetoothSdkSubscription
542
553
 
543
- getGlassesStatus(): Promise<PublicGlassesStatus>
544
- getBluetoothStatus(): Promise<PublicBluetoothStatus>
545
554
  getDefaultDevice(): Promise<Device | null>
546
555
  setDefaultDevice(device: Device | null): Promise<void>
547
556
  clearDefaultDevice(): Promise<void>
@@ -614,8 +623,6 @@ export interface BluetoothSdkPublicModule {
614
623
  ): Promise<void>
615
624
 
616
625
  requestVersionInfo(): Promise<void>
617
- onGlassesStatus(callback: (changed: Partial<PublicGlassesStatus>) => void): () => void
618
- onBluetoothStatus(callback: (changed: Partial<PublicBluetoothStatus>) => void): () => void
619
626
  }
620
627
 
621
628
  // OTA update status types
@@ -711,9 +718,15 @@ export interface CoreSettings {
711
718
  }
712
719
 
713
720
  export interface Device {
721
+ /**
722
+ * Stable app-facing key for this scan result, within the limits of the
723
+ * platform identifier available to the SDK. Do not parse this value; use the
724
+ * typed model, name, address, and rssi fields instead.
725
+ */
714
726
  id: string
715
727
  model: DeviceModel
716
728
  name: string
729
+ /** Platform address/identifier when available: Android Bluetooth address, iOS CoreBluetooth identifier. */
717
730
  address?: string
718
731
  rssi?: number
719
732
  }
package/src/_internal.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * MentraOS-only compatibility entrypoint.
3
3
  *
4
- * Partner apps should import from `@mentra/bluetooth-sdk`. This file is not a
4
+ * Apps should import from `@mentra/bluetooth-sdk`. This file is not a
5
5
  * package export; MentraOS resolves it through its local
6
6
  * `@mentra/bluetooth-sdk-internal` alias while the app is migrated onto the
7
7
  * public SDK surface.
@@ -20,6 +20,7 @@ import {
20
20
  ObservableStoreCategory,
21
21
  PhotoCompression,
22
22
  PhotoSize,
23
+ PublicBluetoothStatus,
23
24
  RgbLedAction,
24
25
  RgbLedColor,
25
26
  ScanModelOptions,
@@ -33,17 +34,18 @@ import {
33
34
  *
34
35
  * This file intentionally lives under `_private` so the package root can expose
35
36
  * a small SDK surface while MentraOS uses its monorepo-only internal alias
36
- * during migration.
37
+ * during migration. Raw status getters/listeners stay here so React hooks can
38
+ * shape native store snapshots before app code sees them.
37
39
  */
38
40
 
39
41
  type GlassesListener = (changed: Partial<GlassesStatus>) => void
40
- type BluetoothStatusListener = (changed: Partial<BluetoothStatus>) => void
42
+ type BluetoothStatusListener = (changed: Partial<PublicBluetoothStatus>) => void
41
43
  type MaybePromise<T> = T | Promise<T>
42
44
 
43
45
  declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEvents> {
44
46
  // Observable Store Functions (native)
45
47
  getGlassesStatus(): Promise<GlassesStatus>
46
- getBluetoothStatus(): Promise<BluetoothStatus>
48
+ getBluetoothStatus(): Promise<PublicBluetoothStatus>
47
49
  getDefaultDevice(): Promise<Device | null>
48
50
  update(category: ObservableStoreCategory, values: object): Promise<void>
49
51
 
@@ -194,7 +196,7 @@ const CAMERA_FOV_SETTINGS: Record<CameraFov, CameraFovSetting> = {
194
196
  wide: {fov: 118, roiPosition: 0},
195
197
  }
196
198
 
197
- function searchResultsForModel(status: Partial<BluetoothStatus>, model: DeviceModel): Device[] {
199
+ function searchResultsForModel(status: Partial<PublicBluetoothStatus>, model: DeviceModel): Device[] {
198
200
  return status.searchResults?.filter((device) => device.model === model) ?? []
199
201
  }
200
202
 
@@ -388,7 +390,7 @@ NativeBluetoothSdkModule.setMicState = function (
388
390
  nativeSetMicState(
389
391
  enabled,
390
392
  useGlassesMic ?? true,
391
- bypassVad ?? false,
393
+ bypassVad ?? true,
392
394
  sendTranscript ?? false,
393
395
  sendLc3Data ?? false,
394
396
  ),