@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.
Files changed (61) hide show
  1. package/README.md +29 -0
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +21 -0
  3. package/android/src/main/java/com/mentra/bluetoothsdk/ConnectedDeviceMatcher.kt +69 -0
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +8 -0
  5. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +66 -1
  7. package/android/src/main/java/com/mentra/bluetoothsdk/NativeNotifications.kt +42 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +38 -1
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/EvenFileService.kt +101 -0
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/EvenNotificationProtocol.kt +113 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +378 -24
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NotificationIds.kt +19 -0
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +11 -0
  14. package/android/src/test/java/com/mentra/bluetoothsdk/ConnectedDeviceMatcherTest.kt +161 -0
  15. package/android/src/test/java/com/mentra/bluetoothsdk/ScanCompletionTest.kt +87 -0
  16. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/EvenFileServiceTest.kt +153 -0
  17. package/build/BluetoothSdk.types.d.ts +57 -0
  18. package/build/BluetoothSdk.types.d.ts.map +1 -1
  19. package/build/BluetoothSdk.types.js.map +1 -1
  20. package/build/_private/BluetoothSdkModule.d.ts +6 -1
  21. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  22. package/build/_private/BluetoothSdkModule.js +28 -49
  23. package/build/_private/BluetoothSdkModule.js.map +1 -1
  24. package/build/_private/scanSession.d.ts +21 -0
  25. package/build/_private/scanSession.d.ts.map +1 -0
  26. package/build/_private/scanSession.js +75 -0
  27. package/build/_private/scanSession.js.map +1 -0
  28. package/build/generated/releaseMetadata.js +5 -5
  29. package/build/generated/releaseMetadata.js.map +1 -1
  30. package/build/index.d.ts +2 -1
  31. package/build/index.d.ts.map +1 -1
  32. package/build/index.js +4 -0
  33. package/build/index.js.map +1 -1
  34. package/build/react/useBluetoothScan.d.ts +2 -1
  35. package/build/react/useBluetoothScan.d.ts.map +1 -1
  36. package/build/react/useBluetoothScan.js +13 -0
  37. package/build/react/useBluetoothScan.js.map +1 -1
  38. package/build/react/useMentraBluetooth.d.ts +2 -1
  39. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  40. package/build/react/useMentraBluetooth.js +1 -0
  41. package/build/react/useMentraBluetooth.js.map +1 -1
  42. package/ios/BluetoothSdkModule.swift +30 -0
  43. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  44. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  45. package/ios/Source/MentraBluetoothSDK.swift +53 -2
  46. package/ios/Source/NativeNotifications.swift +52 -0
  47. package/ios/Source/connection/ScanSession.swift +11 -0
  48. package/ios/Source/internal/BluetoothAvailability.swift +7 -0
  49. package/ios/Source/internal/ConnectedDeviceMatcher.swift +45 -0
  50. package/ios/Source/sgcs/G2.swift +131 -21
  51. package/ios/Source/sgcs/SGCManager.swift +22 -0
  52. package/ios/Tests/ConnectedDeviceMatcherTests.swift +27 -0
  53. package/ios/Tests/NativeNotificationTests.swift +20 -0
  54. package/package.json +1 -1
  55. package/src/BluetoothSdk.types.ts +62 -0
  56. package/src/_private/BluetoothSdkModule.ts +47 -57
  57. package/src/_private/scanSession.ts +88 -0
  58. package/src/generated/releaseMetadata.ts +5 -5
  59. package/src/index.ts +7 -0
  60. package/src/react/useBluetoothScan.ts +12 -1
  61. 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.180",
16
- "releaseSetId": "mentra-3.2.0-dev.180",
17
- "sourceCommit": "1f24e462561fef37b7082204847a541db4a87c41",
18
- "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.180.json",
19
- "otaManifestSha256": "cbf2a1b81430bd34942e2a1764138f6d79197767b4434a2d8c44311768f8b873"
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,