@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.
- package/README.md +63 -18
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +38 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +88 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +11 -5
- package/app.plugin.js +3 -1
- package/build/BluetoothSdk.types.d.ts +20 -7
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +1 -1
- package/build/_internal.js +1 -1
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +5 -4
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +1 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts +4 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +81 -2
- package/build/index.js.map +1 -1
- package/build/react/index.d.ts +5 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +4 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useBluetoothEvent.d.ts +6 -0
- package/build/react/useBluetoothEvent.d.ts.map +1 -0
- package/build/react/useBluetoothEvent.js +21 -0
- package/build/react/useBluetoothEvent.js.map +1 -0
- package/build/react/useBluetoothScan.d.ts +22 -0
- package/build/react/useBluetoothScan.d.ts.map +1 -0
- package/build/react/useBluetoothScan.js +135 -0
- package/build/react/useBluetoothScan.js.map +1 -0
- package/build/react/useBluetoothStatus.d.ts +16 -0
- package/build/react/useBluetoothStatus.d.ts.map +1 -0
- package/build/react/useBluetoothStatus.js +137 -0
- package/build/react/useBluetoothStatus.js.map +1 -0
- package/build/react/useGlassesConnection.d.ts +29 -0
- package/build/react/useGlassesConnection.d.ts.map +1 -0
- package/build/react/useGlassesConnection.js +153 -0
- package/build/react/useGlassesConnection.js.map +1 -0
- package/build/react/useMentraBluetooth.d.ts +98 -0
- package/build/react/useMentraBluetooth.d.ts.map +1 -0
- package/build/react/useMentraBluetooth.js +156 -0
- package/build/react/useMentraBluetooth.js.map +1 -0
- package/ios/BluetoothSdkModule.swift +5 -5
- package/ios/Source/Bridge.swift +42 -8
- package/ios/Source/DeviceManager.swift +0 -1
- package/ios/Source/DeviceStore.swift +1 -1
- package/ios/Source/MentraBluetoothSDK.swift +117 -10
- package/package.json +7 -1
- package/src/BluetoothSdk.types.ts +20 -7
- package/src/_internal.ts +1 -1
- package/src/_private/BluetoothSdkModule.ts +7 -5
- package/src/index.ts +93 -4
- package/src/react/index.ts +24 -0
- package/src/react/useBluetoothEvent.ts +38 -0
- package/src/react/useBluetoothScan.ts +173 -0
- package/src/react/useBluetoothStatus.ts +180 -0
- package/src/react/useGlassesConnection.ts +209 -0
- 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 ??
|
|
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
|
|
593
|
-
sendEvent("mic_pcm",
|
|
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
|
|
598
|
-
sendEvent("mic_lc3",
|
|
597
|
+
public func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 event: MicLc3Event) {
|
|
598
|
+
sendEvent("mic_lc3", event.values)
|
|
599
599
|
}
|
|
600
600
|
|
|
601
601
|
@MainActor
|
package/ios/Source/Bridge.swift
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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",
|
|
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") ??
|
|
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
|
|
1993
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicLc3
|
|
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 _:
|
|
2007
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 _:
|
|
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 =
|
|
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
|
-
|
|
2571
|
-
|
|
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
|
-
|
|
2575
|
-
|
|
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
|
+
"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
|
|
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
|
-
*
|
|
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<
|
|
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<
|
|
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<
|
|
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 ??
|
|
393
|
+
bypassVad ?? true,
|
|
392
394
|
sendTranscript ?? false,
|
|
393
395
|
sendLc3Data ?? false,
|
|
394
396
|
),
|