@mentra/bluetooth-sdk 0.1.3 → 0.1.4
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 +42 -0
- package/app.plugin.js +3 -1
- package/build/react/index.d.ts +5 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +5 -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 +140 -0
- package/build/react/useGlassesConnection.js.map +1 -0
- package/package.json +7 -1
- package/src/react/index.ts +22 -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 +194 -0
|
@@ -0,0 +1,194 @@
|
|
|
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
|
+
if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
autoConnectAttemptedRef.current = true
|
|
105
|
+
void connectDefault().catch(() => undefined)
|
|
106
|
+
}, [defaultDevice, options.autoConnectDefault, status.connected])
|
|
107
|
+
|
|
108
|
+
async function runConnectionAction<T>(
|
|
109
|
+
nextAction: Exclude<GlassesConnectionAction, "idle" | "scanning">,
|
|
110
|
+
operation: () => Promise<T>,
|
|
111
|
+
): Promise<T> {
|
|
112
|
+
setAction(nextAction)
|
|
113
|
+
setOperationError(null)
|
|
114
|
+
try {
|
|
115
|
+
const result = await operation()
|
|
116
|
+
setOperationError(null)
|
|
117
|
+
return result
|
|
118
|
+
} catch (nextError) {
|
|
119
|
+
setOperationError(nextError)
|
|
120
|
+
onErrorRef.current?.(nextError)
|
|
121
|
+
throw nextError
|
|
122
|
+
} finally {
|
|
123
|
+
setAction("idle")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function setDefaultDevice(device: Device | null) {
|
|
128
|
+
await runConnectionAction("connecting", async () => {
|
|
129
|
+
await BluetoothSdk.setDefaultDevice(device)
|
|
130
|
+
await defaultDeviceStorageRef.current?.save(device)
|
|
131
|
+
setDefaultDeviceState(device)
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function clearDefaultDevice() {
|
|
136
|
+
await runConnectionAction("forgetting", async () => {
|
|
137
|
+
await BluetoothSdk.clearDefaultDevice()
|
|
138
|
+
await defaultDeviceStorageRef.current?.save(null)
|
|
139
|
+
setDefaultDeviceState(null)
|
|
140
|
+
scan.selectDevice(null)
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function connect(device?: Device, connectOptions?: ConnectOptions) {
|
|
145
|
+
await runConnectionAction("connecting", async () => {
|
|
146
|
+
const targetDevice = device ?? scan.selectedDevice
|
|
147
|
+
if (targetDevice) {
|
|
148
|
+
await BluetoothSdk.connect(targetDevice, connectOptions)
|
|
149
|
+
if (connectOptions?.saveAsDefault !== false) {
|
|
150
|
+
await defaultDeviceStorageRef.current?.save(targetDevice)
|
|
151
|
+
setDefaultDeviceState(targetDevice)
|
|
152
|
+
}
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
await BluetoothSdk.connectDefault(connectOptions)
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function connectDefault(connectOptions?: ConnectOptions) {
|
|
160
|
+
await runConnectionAction("connecting", async () => {
|
|
161
|
+
await BluetoothSdk.connectDefault(connectOptions)
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function disconnect() {
|
|
166
|
+
await runConnectionAction("disconnecting", async () => {
|
|
167
|
+
await BluetoothSdk.disconnect()
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function forget() {
|
|
172
|
+
await runConnectionAction("forgetting", async () => {
|
|
173
|
+
await BluetoothSdk.forget()
|
|
174
|
+
scan.clearResults()
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const busy = action !== "idle" || scan.scanning || status.loading
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
...status,
|
|
182
|
+
action: scan.scanning ? "scanning" : action,
|
|
183
|
+
busy,
|
|
184
|
+
clearDefaultDevice,
|
|
185
|
+
connect,
|
|
186
|
+
connectDefault,
|
|
187
|
+
defaultDevice,
|
|
188
|
+
disconnect,
|
|
189
|
+
error: operationError ?? scan.error ?? status.error,
|
|
190
|
+
forget,
|
|
191
|
+
scan,
|
|
192
|
+
setDefaultDevice,
|
|
193
|
+
}
|
|
194
|
+
}
|