@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,209 @@
|
|
|
1
|
+
import {useEffect, useRef, useState} from "react"
|
|
2
|
+
|
|
3
|
+
import BluetoothSdk from "../index"
|
|
4
|
+
import type {ConnectOptions, Device, DeviceModel} from "../BluetoothSdk.types"
|
|
5
|
+
|
|
6
|
+
import {useBluetoothScan, type BluetoothScanHookResult} from "./useBluetoothScan"
|
|
7
|
+
import {useBluetoothStatus, type BluetoothStatusHookResult} from "./useBluetoothStatus"
|
|
8
|
+
|
|
9
|
+
export type DefaultDeviceStorage = {
|
|
10
|
+
load: () => Promise<Device | null>
|
|
11
|
+
save: (device: Device | null) => Promise<void>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type GlassesConnectionAction =
|
|
15
|
+
| "idle"
|
|
16
|
+
| "scanning"
|
|
17
|
+
| "connecting"
|
|
18
|
+
| "disconnecting"
|
|
19
|
+
| "forgetting"
|
|
20
|
+
|
|
21
|
+
export type UseGlassesConnectionOptions = {
|
|
22
|
+
autoConnectDefault?: boolean
|
|
23
|
+
defaultDeviceStorage?: DefaultDeviceStorage
|
|
24
|
+
onError?: (error: unknown) => void
|
|
25
|
+
scanModel?: DeviceModel
|
|
26
|
+
scanTimeoutMs?: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type GlassesConnectionHookResult = BluetoothStatusHookResult & {
|
|
30
|
+
action: GlassesConnectionAction
|
|
31
|
+
busy: boolean
|
|
32
|
+
clearDefaultDevice: () => Promise<void>
|
|
33
|
+
connect: (device?: Device, options?: ConnectOptions) => Promise<void>
|
|
34
|
+
connectDefault: (options?: ConnectOptions) => Promise<void>
|
|
35
|
+
defaultDevice: Device | null
|
|
36
|
+
disconnect: () => Promise<void>
|
|
37
|
+
forget: () => Promise<void>
|
|
38
|
+
scan: BluetoothScanHookResult
|
|
39
|
+
setDefaultDevice: (device: Device | null) => Promise<void>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function useGlassesConnection(
|
|
43
|
+
options: UseGlassesConnectionOptions = {},
|
|
44
|
+
): GlassesConnectionHookResult {
|
|
45
|
+
const status = useBluetoothStatus({onError: options.onError})
|
|
46
|
+
const scan = useBluetoothScan({
|
|
47
|
+
model: options.scanModel,
|
|
48
|
+
onError: options.onError,
|
|
49
|
+
timeoutMs: options.scanTimeoutMs,
|
|
50
|
+
})
|
|
51
|
+
const [action, setAction] = useState<GlassesConnectionAction>("idle")
|
|
52
|
+
const [defaultDevice, setDefaultDeviceState] = useState<Device | null>(null)
|
|
53
|
+
const [operationError, setOperationError] = useState<unknown | null>(null)
|
|
54
|
+
const autoConnectAttemptedRef = useRef(false)
|
|
55
|
+
const defaultDeviceStorageRef = useRef(options.defaultDeviceStorage)
|
|
56
|
+
const onErrorRef = useRef(options.onError)
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
defaultDeviceStorageRef.current = options.defaultDeviceStorage
|
|
60
|
+
onErrorRef.current = options.onError
|
|
61
|
+
}, [options.defaultDeviceStorage, options.onError])
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
let mounted = true
|
|
65
|
+
|
|
66
|
+
const loadDefaultDevice = async () => {
|
|
67
|
+
try {
|
|
68
|
+
const [nativeDefaultDevice, storedDefaultDevice] = await Promise.all([
|
|
69
|
+
BluetoothSdk.getDefaultDevice(),
|
|
70
|
+
defaultDeviceStorageRef.current?.load() ?? Promise.resolve(null),
|
|
71
|
+
])
|
|
72
|
+
if (!mounted) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
const nextDefaultDevice = storedDefaultDevice ?? nativeDefaultDevice
|
|
76
|
+
if (nextDefaultDevice) {
|
|
77
|
+
await BluetoothSdk.setDefaultDevice(nextDefaultDevice)
|
|
78
|
+
}
|
|
79
|
+
if (mounted) {
|
|
80
|
+
setDefaultDeviceState(nextDefaultDevice)
|
|
81
|
+
setOperationError(null)
|
|
82
|
+
}
|
|
83
|
+
} catch (nextError) {
|
|
84
|
+
if (!mounted) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
setOperationError(nextError)
|
|
88
|
+
onErrorRef.current?.(nextError)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void loadDefaultDevice()
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
mounted = false
|
|
96
|
+
}
|
|
97
|
+
}, [])
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const subscription = BluetoothSdk.addListener("default_device_changed", (event) => {
|
|
101
|
+
const nextDefaultDevice = event.device ?? null
|
|
102
|
+
setDefaultDeviceState(nextDefaultDevice)
|
|
103
|
+
void defaultDeviceStorageRef.current?.save(nextDefaultDevice).catch((nextError) => {
|
|
104
|
+
setOperationError(nextError)
|
|
105
|
+
onErrorRef.current?.(nextError)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
subscription.remove()
|
|
111
|
+
}
|
|
112
|
+
}, [])
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
autoConnectAttemptedRef.current = true
|
|
120
|
+
void connectDefault().catch(() => undefined)
|
|
121
|
+
}, [defaultDevice, options.autoConnectDefault, status.connected])
|
|
122
|
+
|
|
123
|
+
async function runConnectionAction<T>(
|
|
124
|
+
nextAction: Exclude<GlassesConnectionAction, "idle" | "scanning">,
|
|
125
|
+
operation: () => Promise<T>,
|
|
126
|
+
): Promise<T> {
|
|
127
|
+
setAction(nextAction)
|
|
128
|
+
setOperationError(null)
|
|
129
|
+
try {
|
|
130
|
+
const result = await operation()
|
|
131
|
+
setOperationError(null)
|
|
132
|
+
return result
|
|
133
|
+
} catch (nextError) {
|
|
134
|
+
setOperationError(nextError)
|
|
135
|
+
onErrorRef.current?.(nextError)
|
|
136
|
+
throw nextError
|
|
137
|
+
} finally {
|
|
138
|
+
setAction("idle")
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function setDefaultDevice(device: Device | null) {
|
|
143
|
+
await runConnectionAction("connecting", async () => {
|
|
144
|
+
await BluetoothSdk.setDefaultDevice(device)
|
|
145
|
+
await defaultDeviceStorageRef.current?.save(device)
|
|
146
|
+
setDefaultDeviceState(device)
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function clearDefaultDevice() {
|
|
151
|
+
await runConnectionAction("forgetting", async () => {
|
|
152
|
+
await BluetoothSdk.clearDefaultDevice()
|
|
153
|
+
await defaultDeviceStorageRef.current?.save(null)
|
|
154
|
+
setDefaultDeviceState(null)
|
|
155
|
+
scan.selectDevice(null)
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function connect(device?: Device, connectOptions?: ConnectOptions) {
|
|
160
|
+
await runConnectionAction("connecting", async () => {
|
|
161
|
+
const targetDevice = device ?? scan.selectedDevice
|
|
162
|
+
if (targetDevice) {
|
|
163
|
+
await BluetoothSdk.connect(targetDevice, connectOptions)
|
|
164
|
+
if (connectOptions?.saveAsDefault !== false) {
|
|
165
|
+
await defaultDeviceStorageRef.current?.save(targetDevice)
|
|
166
|
+
setDefaultDeviceState(targetDevice)
|
|
167
|
+
}
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
await BluetoothSdk.connectDefault(connectOptions)
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function connectDefault(connectOptions?: ConnectOptions) {
|
|
175
|
+
await runConnectionAction("connecting", async () => {
|
|
176
|
+
await BluetoothSdk.connectDefault(connectOptions)
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function disconnect() {
|
|
181
|
+
await runConnectionAction("disconnecting", async () => {
|
|
182
|
+
await BluetoothSdk.disconnect()
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function forget() {
|
|
187
|
+
await runConnectionAction("forgetting", async () => {
|
|
188
|
+
await BluetoothSdk.forget()
|
|
189
|
+
scan.clearResults()
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const busy = action !== "idle" || scan.scanning || status.loading
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
...status,
|
|
197
|
+
action: scan.scanning ? "scanning" : action,
|
|
198
|
+
busy,
|
|
199
|
+
clearDefaultDevice,
|
|
200
|
+
connect,
|
|
201
|
+
connectDefault,
|
|
202
|
+
defaultDevice,
|
|
203
|
+
disconnect,
|
|
204
|
+
error: operationError ?? scan.error ?? status.error,
|
|
205
|
+
forget,
|
|
206
|
+
scan,
|
|
207
|
+
setDefaultDevice,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import {useState} from "react"
|
|
2
|
+
|
|
3
|
+
import BluetoothSdk from "../index"
|
|
4
|
+
import {
|
|
5
|
+
DeviceModels,
|
|
6
|
+
createDisconnectedGlassesStatus,
|
|
7
|
+
isConnectedGlassesConnectionStatus,
|
|
8
|
+
isReadyGlassesConnectionStatus,
|
|
9
|
+
} from "../BluetoothSdk.types"
|
|
10
|
+
import type {
|
|
11
|
+
ConnectOptions,
|
|
12
|
+
Device,
|
|
13
|
+
DeviceModel,
|
|
14
|
+
GalleryMode,
|
|
15
|
+
GlassesConnectionStatus,
|
|
16
|
+
HotspotStatus,
|
|
17
|
+
PublicBluetoothStatus,
|
|
18
|
+
PublicGlassesStatus,
|
|
19
|
+
MicMode,
|
|
20
|
+
WifiSearchResult,
|
|
21
|
+
WifiStatus,
|
|
22
|
+
} from "../BluetoothSdk.types"
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
useGlassesConnection,
|
|
26
|
+
type DefaultDeviceStorage,
|
|
27
|
+
type GlassesConnectionHookResult,
|
|
28
|
+
} from "./useGlassesConnection"
|
|
29
|
+
|
|
30
|
+
export type BatteryState = {
|
|
31
|
+
charging: boolean
|
|
32
|
+
level: number | null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ConnectedGlassesInfo = {
|
|
36
|
+
appVersion?: string
|
|
37
|
+
bluetoothName?: string
|
|
38
|
+
buildNumber?: string
|
|
39
|
+
color?: string
|
|
40
|
+
deviceModel?: string
|
|
41
|
+
firmwareVersion?: string
|
|
42
|
+
serialNumber?: string
|
|
43
|
+
style?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type FirmwareInfo = {
|
|
47
|
+
appVersion?: string
|
|
48
|
+
buildNumber?: string
|
|
49
|
+
source:
|
|
50
|
+
| "app"
|
|
51
|
+
| "bes"
|
|
52
|
+
| "device"
|
|
53
|
+
| "firmware"
|
|
54
|
+
| "left"
|
|
55
|
+
| "mtk"
|
|
56
|
+
| "right"
|
|
57
|
+
| "unknown"
|
|
58
|
+
version: string | null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type SignalState = {
|
|
62
|
+
strengthDbm: number | null
|
|
63
|
+
updatedAt: number | null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type GlassesRuntimeState =
|
|
67
|
+
| {
|
|
68
|
+
connected: false
|
|
69
|
+
connection: Exclude<GlassesConnectionStatus, {state: "connected"}>
|
|
70
|
+
ready: false
|
|
71
|
+
}
|
|
72
|
+
| {
|
|
73
|
+
battery: BatteryState
|
|
74
|
+
connected: true
|
|
75
|
+
connection: Extract<GlassesConnectionStatus, {state: "connected"}>
|
|
76
|
+
device: ConnectedGlassesInfo
|
|
77
|
+
firmware: FirmwareInfo
|
|
78
|
+
hotspot: HotspotStatus
|
|
79
|
+
ready: boolean
|
|
80
|
+
signal: SignalState
|
|
81
|
+
wifi: WifiStatus
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type GalleryModeState = {
|
|
85
|
+
applying: boolean
|
|
86
|
+
desired: GalleryMode
|
|
87
|
+
error: unknown | null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type PhoneSdkRuntimeState = {
|
|
91
|
+
currentMic: MicMode | null
|
|
92
|
+
defaultDevice: Device | null
|
|
93
|
+
galleryMode: GalleryModeState
|
|
94
|
+
lastLog: string[]
|
|
95
|
+
micRanking: MicMode[]
|
|
96
|
+
otherBluetoothConnected: boolean
|
|
97
|
+
searching: boolean
|
|
98
|
+
searchingController: boolean
|
|
99
|
+
systemMicUnavailable: boolean
|
|
100
|
+
wifiScanResults: WifiSearchResult[]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type ScanController = {
|
|
104
|
+
active: boolean
|
|
105
|
+
clear: () => void
|
|
106
|
+
devices: Device[]
|
|
107
|
+
error: unknown | null
|
|
108
|
+
model: DeviceModel
|
|
109
|
+
selectedDevice: Device | null
|
|
110
|
+
selectDevice: (device: Device | null) => void
|
|
111
|
+
setModel: (model: DeviceModel) => void
|
|
112
|
+
start: (model?: DeviceModel) => Promise<Device[]>
|
|
113
|
+
stop: () => Promise<void>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type UseMentraBluetoothOptions = {
|
|
117
|
+
autoConnectDefault?: boolean
|
|
118
|
+
defaultDeviceStorage?: DefaultDeviceStorage
|
|
119
|
+
defaultModel?: DeviceModel
|
|
120
|
+
onError?: (error: unknown) => void
|
|
121
|
+
scanTimeoutMs?: number
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type MentraBluetoothSession = {
|
|
125
|
+
busy: boolean
|
|
126
|
+
clearDefaultDevice: () => Promise<void>
|
|
127
|
+
connect: (device?: Device, options?: ConnectOptions) => Promise<void>
|
|
128
|
+
connectDefault: (options?: ConnectOptions) => Promise<void>
|
|
129
|
+
defaultDevice: Device | null
|
|
130
|
+
disconnect: () => Promise<void>
|
|
131
|
+
error: unknown | null
|
|
132
|
+
glasses: GlassesRuntimeState
|
|
133
|
+
refresh: () => Promise<void>
|
|
134
|
+
scan: ScanController
|
|
135
|
+
sdk: PhoneSdkRuntimeState
|
|
136
|
+
setDefaultDevice: (device: Device | null) => Promise<void>
|
|
137
|
+
setGalleryMode: (mode: GalleryMode) => Promise<void>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function stringValue(value: unknown): string | undefined {
|
|
141
|
+
return typeof value === "string" && value.length > 0 ? value : undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function numberValue(value: unknown): number | null {
|
|
145
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function firmwareInfo(status: Partial<PublicGlassesStatus>): FirmwareInfo {
|
|
149
|
+
const firmwareSources = [
|
|
150
|
+
["firmwareVersion", "firmware"],
|
|
151
|
+
["deviceFirmwareVersion", "device"],
|
|
152
|
+
["rightFirmwareVersion", "right"],
|
|
153
|
+
["leftFirmwareVersion", "left"],
|
|
154
|
+
["besFirmwareVersion", "bes"],
|
|
155
|
+
["mtkFirmwareVersion", "mtk"],
|
|
156
|
+
["appVersion", "app"],
|
|
157
|
+
] as const
|
|
158
|
+
|
|
159
|
+
for (const [key, source] of firmwareSources) {
|
|
160
|
+
const value = stringValue((status as Record<string, unknown>)[key])
|
|
161
|
+
if (value) {
|
|
162
|
+
return {
|
|
163
|
+
appVersion: stringValue(status.appVersion),
|
|
164
|
+
buildNumber: stringValue(status.buildNumber),
|
|
165
|
+
source,
|
|
166
|
+
version: value,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
appVersion: stringValue(status.appVersion),
|
|
173
|
+
buildNumber: stringValue(status.buildNumber),
|
|
174
|
+
source: "unknown",
|
|
175
|
+
version: null,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function batteryState(status: Partial<PublicGlassesStatus>): BatteryState {
|
|
180
|
+
const level = typeof status.batteryLevel === "number" && status.batteryLevel >= 0 ? status.batteryLevel : null
|
|
181
|
+
return {
|
|
182
|
+
charging: status.charging ?? false,
|
|
183
|
+
level,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function connectedGlassesInfo(status: Partial<PublicGlassesStatus>): ConnectedGlassesInfo {
|
|
188
|
+
return {
|
|
189
|
+
appVersion: stringValue(status.appVersion),
|
|
190
|
+
bluetoothName: stringValue(status.bluetoothName),
|
|
191
|
+
buildNumber: stringValue(status.buildNumber),
|
|
192
|
+
color: stringValue(status.color),
|
|
193
|
+
deviceModel: stringValue(status.deviceModel),
|
|
194
|
+
firmwareVersion: stringValue(status.firmwareVersion),
|
|
195
|
+
serialNumber: stringValue(status.serialNumber),
|
|
196
|
+
style: stringValue(status.style),
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function runtimeGlassesState(status: Partial<PublicGlassesStatus>): GlassesRuntimeState {
|
|
201
|
+
const connection = status.connection ?? createDisconnectedGlassesStatus().connection ?? {state: "disconnected"}
|
|
202
|
+
|
|
203
|
+
if (!isConnectedGlassesConnectionStatus(connection)) {
|
|
204
|
+
return {
|
|
205
|
+
connected: false,
|
|
206
|
+
connection,
|
|
207
|
+
ready: false,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
battery: batteryState(status),
|
|
213
|
+
connected: true,
|
|
214
|
+
connection,
|
|
215
|
+
device: connectedGlassesInfo(status),
|
|
216
|
+
firmware: firmwareInfo(status),
|
|
217
|
+
hotspot: status.hotspot ?? {state: "disabled"},
|
|
218
|
+
ready: isReadyGlassesConnectionStatus(connection),
|
|
219
|
+
signal: {
|
|
220
|
+
strengthDbm: numberValue((status as Record<string, unknown>).signalStrength),
|
|
221
|
+
updatedAt: numberValue((status as Record<string, unknown>).signalStrengthUpdatedAt),
|
|
222
|
+
},
|
|
223
|
+
wifi: status.wifi ?? {state: "disconnected"},
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function phoneSdkState(
|
|
228
|
+
status: Partial<PublicBluetoothStatus>,
|
|
229
|
+
defaultDevice: Device | null,
|
|
230
|
+
galleryMode: GalleryModeState,
|
|
231
|
+
): PhoneSdkRuntimeState {
|
|
232
|
+
return {
|
|
233
|
+
currentMic: status.currentMic || null,
|
|
234
|
+
defaultDevice,
|
|
235
|
+
galleryMode,
|
|
236
|
+
lastLog: status.lastLog ?? [],
|
|
237
|
+
micRanking: status.micRanking ?? [],
|
|
238
|
+
otherBluetoothConnected: status.otherBtConnected ?? false,
|
|
239
|
+
searching: status.searching ?? false,
|
|
240
|
+
searchingController: status.searchingController ?? false,
|
|
241
|
+
systemMicUnavailable: status.systemMicUnavailable ?? false,
|
|
242
|
+
wifiScanResults: status.wifiScanResults ?? [],
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function scanController(connection: GlassesConnectionHookResult): ScanController {
|
|
247
|
+
return {
|
|
248
|
+
active: connection.scan.scanning,
|
|
249
|
+
clear: connection.scan.clearResults,
|
|
250
|
+
devices: connection.scan.devices,
|
|
251
|
+
error: connection.scan.error,
|
|
252
|
+
model: connection.scan.model,
|
|
253
|
+
selectedDevice: connection.scan.selectedDevice,
|
|
254
|
+
selectDevice: connection.scan.selectDevice,
|
|
255
|
+
setModel: connection.scan.setModel,
|
|
256
|
+
start: connection.scan.startScan,
|
|
257
|
+
stop: connection.scan.stopScan,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function useMentraBluetooth(options: UseMentraBluetoothOptions = {}): MentraBluetoothSession {
|
|
262
|
+
const connection = useGlassesConnection({
|
|
263
|
+
autoConnectDefault: options.autoConnectDefault,
|
|
264
|
+
defaultDeviceStorage: options.defaultDeviceStorage,
|
|
265
|
+
onError: options.onError,
|
|
266
|
+
scanModel: options.defaultModel ?? DeviceModels.MentraLive,
|
|
267
|
+
scanTimeoutMs: options.scanTimeoutMs,
|
|
268
|
+
})
|
|
269
|
+
const [galleryModeApplying, setGalleryModeApplying] = useState(false)
|
|
270
|
+
const [galleryModeError, setGalleryModeError] = useState<unknown | null>(null)
|
|
271
|
+
|
|
272
|
+
async function setGalleryMode(mode: GalleryMode) {
|
|
273
|
+
setGalleryModeApplying(true)
|
|
274
|
+
setGalleryModeError(null)
|
|
275
|
+
try {
|
|
276
|
+
await BluetoothSdk.setGalleryMode(mode)
|
|
277
|
+
} catch (error) {
|
|
278
|
+
setGalleryModeError(error)
|
|
279
|
+
options.onError?.(error)
|
|
280
|
+
throw error
|
|
281
|
+
} finally {
|
|
282
|
+
setGalleryModeApplying(false)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const galleryMode: GalleryModeState = {
|
|
287
|
+
applying: galleryModeApplying,
|
|
288
|
+
desired: connection.bluetoothStatus.galleryModeAuto === false ? "manual" : "auto",
|
|
289
|
+
error: galleryModeError,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
busy: connection.busy || galleryModeApplying,
|
|
294
|
+
clearDefaultDevice: connection.clearDefaultDevice,
|
|
295
|
+
connect: connection.connect,
|
|
296
|
+
connectDefault: connection.connectDefault,
|
|
297
|
+
defaultDevice: connection.defaultDevice,
|
|
298
|
+
disconnect: connection.disconnect,
|
|
299
|
+
error: galleryModeError ?? connection.error,
|
|
300
|
+
glasses: runtimeGlassesState(connection.glassesStatus),
|
|
301
|
+
refresh: connection.refresh,
|
|
302
|
+
scan: scanController(connection),
|
|
303
|
+
sdk: phoneSdkState(connection.bluetoothStatus, connection.defaultDevice, galleryMode),
|
|
304
|
+
setDefaultDevice: connection.setDefaultDevice,
|
|
305
|
+
setGalleryMode,
|
|
306
|
+
}
|
|
307
|
+
}
|