@mentra/bluetooth-sdk 3.2.0-dev.180 → 3.2.0-dev.187
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 +29 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +21 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/ConnectedDeviceMatcher.kt +69 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +66 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/NativeNotifications.kt +42 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +38 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/EvenFileService.kt +101 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/EvenNotificationProtocol.kt +113 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +378 -24
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NotificationIds.kt +19 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +11 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/ConnectedDeviceMatcherTest.kt +161 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/ScanCompletionTest.kt +87 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/EvenFileServiceTest.kt +153 -0
- package/build/BluetoothSdk.types.d.ts +57 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +6 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +28 -49
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/scanSession.d.ts +21 -0
- package/build/_private/scanSession.d.ts.map +1 -0
- package/build/_private/scanSession.js +75 -0
- package/build/_private/scanSession.js.map +1 -0
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -0
- package/build/index.js.map +1 -1
- package/build/react/useBluetoothScan.d.ts +2 -1
- package/build/react/useBluetoothScan.d.ts.map +1 -1
- package/build/react/useBluetoothScan.js +13 -0
- package/build/react/useBluetoothScan.js.map +1 -1
- package/build/react/useMentraBluetooth.d.ts +2 -1
- package/build/react/useMentraBluetooth.d.ts.map +1 -1
- package/build/react/useMentraBluetooth.js +1 -0
- package/build/react/useMentraBluetooth.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +30 -0
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
- package/ios/Source/MentraBluetoothSDK.swift +53 -2
- package/ios/Source/NativeNotifications.swift +52 -0
- package/ios/Source/connection/ScanSession.swift +11 -0
- package/ios/Source/internal/BluetoothAvailability.swift +7 -0
- package/ios/Source/internal/ConnectedDeviceMatcher.swift +45 -0
- package/ios/Source/sgcs/G2.swift +131 -21
- package/ios/Source/sgcs/SGCManager.swift +22 -0
- package/ios/Tests/ConnectedDeviceMatcherTests.swift +27 -0
- package/ios/Tests/NativeNotificationTests.swift +20 -0
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +62 -0
- package/src/_private/BluetoothSdkModule.ts +47 -57
- package/src/_private/scanSession.ts +88 -0
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/index.ts +7 -0
- package/src/react/useBluetoothScan.ts +12 -1
- package/src/react/useMentraBluetooth.ts +2 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type {Device, DeviceModel, ScanDiagnostic} from "../BluetoothSdk.types"
|
|
2
|
+
|
|
3
|
+
interface ScanTransport {
|
|
4
|
+
start: (model: DeviceModel) => Promise<void>
|
|
5
|
+
stop: () => Promise<void>
|
|
6
|
+
subscribe: (onResults: (devices: Device[]) => void) => () => void
|
|
7
|
+
getResults: () => Promise<Device[]>
|
|
8
|
+
getDiagnostic?: () => Promise<ScanDiagnostic | null>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ScanSessionOptions {
|
|
12
|
+
model: DeviceModel
|
|
13
|
+
timeoutMs: number
|
|
14
|
+
onResults?: (devices: Device[]) => void
|
|
15
|
+
onDiagnostic?: (diagnostic: ScanDiagnostic) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A scan has one completion; optional diagnostics cannot turn it into a failure. */
|
|
19
|
+
export function createScanSession(transport: ScanTransport, options: ScanSessionOptions) {
|
|
20
|
+
let cancel: () => Promise<void> = async () => {}
|
|
21
|
+
const result = new Promise<Device[]>((resolve, reject) => {
|
|
22
|
+
let latestResults: Device[] = []
|
|
23
|
+
let ending = false
|
|
24
|
+
let cancelled = false
|
|
25
|
+
let startTask: Promise<void> | undefined
|
|
26
|
+
let stopTask: Promise<void> = Promise.resolve()
|
|
27
|
+
let removeListener = () => {}
|
|
28
|
+
let timeout: ReturnType<typeof setTimeout> | undefined
|
|
29
|
+
|
|
30
|
+
const emit = (devices: Device[]) => {
|
|
31
|
+
if (ending) return
|
|
32
|
+
latestResults = devices
|
|
33
|
+
options.onResults?.([...devices])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const finish = async (error?: unknown) => {
|
|
37
|
+
if (ending) return
|
|
38
|
+
ending = true
|
|
39
|
+
clearTimeout(timeout)
|
|
40
|
+
removeListener()
|
|
41
|
+
stopTask = (async () => {
|
|
42
|
+
try {
|
|
43
|
+
await startTask
|
|
44
|
+
await transport.stop()
|
|
45
|
+
} catch {
|
|
46
|
+
// Failed starts and stops do not change the completion contract.
|
|
47
|
+
}
|
|
48
|
+
})()
|
|
49
|
+
await stopTask
|
|
50
|
+
if (error !== undefined) {
|
|
51
|
+
reject(error)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
// Query only at natural, empty completion. A cancellation during the
|
|
55
|
+
// native lookup must also suppress the pending advisory.
|
|
56
|
+
if (!cancelled && latestResults.length === 0 && options.onDiagnostic && transport.getDiagnostic) {
|
|
57
|
+
try {
|
|
58
|
+
const diagnostic = await transport.getDiagnostic()
|
|
59
|
+
if (!cancelled && diagnostic) options.onDiagnostic(diagnostic)
|
|
60
|
+
} catch {
|
|
61
|
+
// Optional platform access or advisory handling cannot fail a scan.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
resolve([...latestResults])
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
cancel = () => {
|
|
68
|
+
cancelled = true
|
|
69
|
+
void finish()
|
|
70
|
+
return stopTask
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void (async () => {
|
|
74
|
+
try {
|
|
75
|
+
removeListener = transport.subscribe(emit)
|
|
76
|
+
emit([])
|
|
77
|
+
timeout = setTimeout(() => void finish(), options.timeoutMs)
|
|
78
|
+
startTask = Promise.resolve().then(() => transport.start(options.model))
|
|
79
|
+
await startTask
|
|
80
|
+
if (ending) return
|
|
81
|
+
emit(await transport.getResults())
|
|
82
|
+
} catch (error) {
|
|
83
|
+
void finish(error)
|
|
84
|
+
}
|
|
85
|
+
})()
|
|
86
|
+
})
|
|
87
|
+
return {result, cancel: () => cancel()}
|
|
88
|
+
}
|
|
@@ -12,9 +12,9 @@ export interface BluetoothSdkReleaseMetadata {
|
|
|
12
12
|
export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
14
|
"familyBaseVersion": "3.2.0",
|
|
15
|
-
"releaseIdentity": "3.2.0-dev.
|
|
16
|
-
"releaseSetId": "mentra-3.2.0-dev.
|
|
17
|
-
"sourceCommit": "
|
|
18
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
19
|
-
"otaManifestSha256": "
|
|
15
|
+
"releaseIdentity": "3.2.0-dev.187",
|
|
16
|
+
"releaseSetId": "mentra-3.2.0-dev.187",
|
|
17
|
+
"sourceCommit": "d8b34892f11593baa3c075b94380253b06eb874d",
|
|
18
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.187.json",
|
|
19
|
+
"otaManifestSha256": "cf21f4ab6347acb12eefa38d2f0a3b433bc0e05428c527bcda57a8693d7a72f9"
|
|
20
20
|
})
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,8 @@ import type {
|
|
|
10
10
|
import {getReleaseChangelogs} from "./changelogs"
|
|
11
11
|
|
|
12
12
|
const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
|
|
13
|
+
"native_notification_status",
|
|
14
|
+
"native_notification_delivery",
|
|
13
15
|
"log",
|
|
14
16
|
"device_discovered",
|
|
15
17
|
"default_device_changed",
|
|
@@ -85,6 +87,8 @@ const bindPublicMethod = <K extends keyof BluetoothSdkPublicModule>(name: K): Bl
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
|
|
90
|
+
configureNativeNotifications: bindPublicMethod("configureNativeNotifications"),
|
|
91
|
+
getNativeNotificationStatus: bindPublicMethod("getNativeNotificationStatus"),
|
|
88
92
|
addListener,
|
|
89
93
|
getGlassesStatus: bindPublicMethod("getGlassesStatus"),
|
|
90
94
|
getBluetoothStatus: bindPublicMethod("getBluetoothStatus"),
|
|
@@ -276,6 +280,7 @@ export type {
|
|
|
276
280
|
RgbLedControlResponseEvent,
|
|
277
281
|
RgbLedControlSuccessResponseEvent,
|
|
278
282
|
ScanModelOptions,
|
|
283
|
+
ScanDiagnostic,
|
|
279
284
|
ScanOptions,
|
|
280
285
|
ScanResultsCallback,
|
|
281
286
|
SpeakingStatusEvent,
|
|
@@ -309,3 +314,5 @@ export type {
|
|
|
309
314
|
WifiStatus,
|
|
310
315
|
WifiStatusChangeEvent,
|
|
311
316
|
} from "./BluetoothSdk.types"
|
|
317
|
+
|
|
318
|
+
export type {NativeNotificationConfig, NativeNotificationStatus, NativeNotificationDelivery} from "./BluetoothSdk.types"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {useEffect, useRef, useState} from "react"
|
|
2
2
|
|
|
3
3
|
import BluetoothSdk, {DeviceModels} from "../index"
|
|
4
|
-
import type {Device, DeviceModel} from "../BluetoothSdk.types"
|
|
4
|
+
import type {Device, DeviceModel, ScanDiagnostic} from "../BluetoothSdk.types"
|
|
5
5
|
|
|
6
6
|
export type BluetoothScanDedupe = "id" | "name" | ((device: Device) => string)
|
|
7
7
|
|
|
@@ -15,6 +15,7 @@ export type UseBluetoothScanOptions = {
|
|
|
15
15
|
export type BluetoothScanHookResult = {
|
|
16
16
|
clearResults: () => void
|
|
17
17
|
devices: Device[]
|
|
18
|
+
diagnostic: ScanDiagnostic | null
|
|
18
19
|
error: unknown | null
|
|
19
20
|
model: DeviceModel
|
|
20
21
|
scanning: boolean
|
|
@@ -61,6 +62,7 @@ function hasDevice(devices: Device[], selectedDevice: Device | null, dedupe: Blu
|
|
|
61
62
|
|
|
62
63
|
export function useBluetoothScan(options: UseBluetoothScanOptions = {}): BluetoothScanHookResult {
|
|
63
64
|
const [devices, setDevices] = useState<Device[]>([])
|
|
65
|
+
const [diagnostic, setDiagnostic] = useState<ScanDiagnostic | null>(null)
|
|
64
66
|
const [error, setError] = useState<unknown | null>(null)
|
|
65
67
|
const [model, setModelState] = useState<DeviceModel>(options.model ?? DeviceModels.MentraLive)
|
|
66
68
|
const [scanning, setScanning] = useState(false)
|
|
@@ -98,11 +100,13 @@ export function useBluetoothScan(options: UseBluetoothScanOptions = {}): Bluetoo
|
|
|
98
100
|
}, [])
|
|
99
101
|
|
|
100
102
|
function clearResults() {
|
|
103
|
+
setDiagnostic(null)
|
|
101
104
|
setDevices([])
|
|
102
105
|
selectDevice(null)
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
function setModel(nextModel: DeviceModel) {
|
|
109
|
+
if (scanningRef.current) void stopScan()
|
|
106
110
|
setModelState(nextModel)
|
|
107
111
|
clearResults()
|
|
108
112
|
}
|
|
@@ -116,6 +120,7 @@ export function useBluetoothScan(options: UseBluetoothScanOptions = {}): Bluetoo
|
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
setError(null)
|
|
123
|
+
setDiagnostic(null)
|
|
119
124
|
setScanning(true)
|
|
120
125
|
scanningRef.current = true
|
|
121
126
|
setDevices([])
|
|
@@ -124,10 +129,14 @@ export function useBluetoothScan(options: UseBluetoothScanOptions = {}): Bluetoo
|
|
|
124
129
|
try {
|
|
125
130
|
const nextDevices = await BluetoothSdk.scan(scanModel, {
|
|
126
131
|
...(timeoutMsRef.current == null ? {} : {timeoutMs: timeoutMsRef.current}),
|
|
132
|
+
onDiagnostic: (hint) => {
|
|
133
|
+
if (activeScanRef.current === scanId) setDiagnostic(hint)
|
|
134
|
+
},
|
|
127
135
|
onResults: (results) => {
|
|
128
136
|
if (activeScanRef.current !== scanId) {
|
|
129
137
|
return
|
|
130
138
|
}
|
|
139
|
+
if (results.length > 0) setDiagnostic(null)
|
|
131
140
|
setDevices(dedupeDevices(results, dedupeRef.current))
|
|
132
141
|
},
|
|
133
142
|
})
|
|
@@ -152,6 +161,7 @@ export function useBluetoothScan(options: UseBluetoothScanOptions = {}): Bluetoo
|
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
async function stopScan() {
|
|
164
|
+
setDiagnostic(null)
|
|
155
165
|
activeScanRef.current += 1
|
|
156
166
|
setScanning(false)
|
|
157
167
|
scanningRef.current = false
|
|
@@ -161,6 +171,7 @@ export function useBluetoothScan(options: UseBluetoothScanOptions = {}): Bluetoo
|
|
|
161
171
|
return {
|
|
162
172
|
clearResults,
|
|
163
173
|
devices,
|
|
174
|
+
diagnostic,
|
|
164
175
|
error,
|
|
165
176
|
model,
|
|
166
177
|
scanning,
|
|
@@ -106,6 +106,7 @@ export type ScanController = {
|
|
|
106
106
|
active: boolean
|
|
107
107
|
clear: () => void
|
|
108
108
|
devices: Device[]
|
|
109
|
+
diagnostic: GlassesConnectionHookResult["scan"]["diagnostic"]
|
|
109
110
|
error: unknown | null
|
|
110
111
|
model: DeviceModel
|
|
111
112
|
selectedDevice: Device | null
|
|
@@ -252,6 +253,7 @@ function scanController(connection: GlassesConnectionHookResult): ScanController
|
|
|
252
253
|
active: connection.scan.scanning,
|
|
253
254
|
clear: connection.scan.clearResults,
|
|
254
255
|
devices: connection.scan.devices,
|
|
256
|
+
diagnostic: connection.scan.diagnostic,
|
|
255
257
|
error: connection.scan.error,
|
|
256
258
|
model: connection.scan.model,
|
|
257
259
|
selectedDevice: connection.scan.selectedDevice,
|