@mentra/bluetooth-sdk 0.1.4 → 0.1.6

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 (72) hide show
  1. package/README.md +61 -42
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +20 -12
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +38 -12
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +2 -1
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +45 -51
  6. package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +125 -0
  7. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +187 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +60 -0
  10. package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +132 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +39 -0
  12. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +605 -0
  13. package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +199 -0
  14. package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +173 -0
  15. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +348 -0
  16. package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +75 -0
  17. package/build/BluetoothSdk.types.d.ts +24 -7
  18. package/build/BluetoothSdk.types.d.ts.map +1 -1
  19. package/build/BluetoothSdk.types.js.map +1 -1
  20. package/build/_internal.d.ts +1 -1
  21. package/build/_internal.js +1 -1
  22. package/build/_internal.js.map +1 -1
  23. package/build/_private/BluetoothSdkModule.d.ts +5 -4
  24. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  25. package/build/_private/BluetoothSdkModule.js +4 -1
  26. package/build/_private/BluetoothSdkModule.js.map +1 -1
  27. package/build/index.d.ts +4 -3
  28. package/build/index.d.ts.map +1 -1
  29. package/build/index.js +81 -2
  30. package/build/index.js.map +1 -1
  31. package/build/react/index.d.ts +2 -2
  32. package/build/react/index.d.ts.map +1 -1
  33. package/build/react/index.js +1 -2
  34. package/build/react/index.js.map +1 -1
  35. package/build/react/useBluetoothStatus.js +1 -1
  36. package/build/react/useBluetoothStatus.js.map +1 -1
  37. package/build/react/useGlassesConnection.d.ts.map +1 -1
  38. package/build/react/useGlassesConnection.js +13 -0
  39. package/build/react/useGlassesConnection.js.map +1 -1
  40. package/build/react/useMentraBluetooth.d.ts +98 -0
  41. package/build/react/useMentraBluetooth.d.ts.map +1 -0
  42. package/build/react/useMentraBluetooth.js +156 -0
  43. package/build/react/useMentraBluetooth.js.map +1 -0
  44. package/ios/BluetoothSdkModule.swift +9 -9
  45. package/ios/Source/Audio/AudioModels.swift +159 -0
  46. package/ios/Source/Bridge.swift +42 -8
  47. package/ios/Source/Camera/CameraModels.swift +246 -0
  48. package/ios/Source/Connection/ScanSession.swift +27 -0
  49. package/ios/Source/DeviceManager.swift +0 -1
  50. package/ios/Source/DeviceStore.swift +2 -1
  51. package/ios/Source/Errors/BluetoothError.swift +19 -0
  52. package/ios/Source/Events/BluetoothEvents.swift +115 -0
  53. package/ios/Source/Internal/BluetoothAvailability.swift +58 -0
  54. package/ios/Source/Internal/ValueParsing.swift +90 -0
  55. package/ios/Source/MentraBluetoothSDK.swift +32 -2040
  56. package/ios/Source/Requests/DisplayRequests.swift +58 -0
  57. package/ios/Source/Status/DeviceStatus.swift +463 -0
  58. package/ios/Source/Status/RuntimeState.swift +350 -0
  59. package/ios/Source/Status/WifiHotspotStatus.swift +326 -0
  60. package/ios/Source/Streaming/StreamModels.swift +436 -0
  61. package/ios/Source/Types/DeviceModels.swift +134 -0
  62. package/ios/Source/sgcs/G2.swift +97 -36
  63. package/package.json +1 -1
  64. package/src/BluetoothSdk.types.ts +24 -7
  65. package/src/_internal.ts +1 -1
  66. package/src/_private/BluetoothSdkModule.ts +10 -5
  67. package/src/index.ts +93 -4
  68. package/src/react/index.ts +13 -11
  69. package/src/react/useBluetoothStatus.ts +1 -1
  70. package/src/react/useGlassesConnection.ts +15 -0
  71. package/src/react/useMentraBluetooth.ts +307 -0
  72. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1725
@@ -1062,6 +1062,8 @@ class G2: NSObject, SGCManager {
1062
1062
  private var rightAuthenticated: Bool = false
1063
1063
  private var isDisconnecting = false
1064
1064
  private var pairingTimeoutTimer: DispatchWorkItem?
1065
+ private var useEvenDashboard = true
1066
+ private var dashboardShowing = 0
1065
1067
 
1066
1068
  /// Device search
1067
1069
  var DEVICE_SEARCH_ID = "NOT_SET"
@@ -1598,11 +1600,11 @@ class G2: NSObject, SGCManager {
1598
1600
  func sendTextWall(_ text: String) {
1599
1601
  // Bridge.log("G2: sendTextWall(\(text.prefix(50))...)")
1600
1602
 
1601
- // ignore events while the dashboard is open:
1602
- // let isHeadUp = DeviceStore.shared.get("glasses", "headUp") as? Bool ?? false
1603
- // if isHeadUp {
1604
- // return
1605
- // }
1603
+ // ignore events while the ER dashboard is open:
1604
+ let useNativeDashboard = DeviceStore.shared.get("bluetooth", "use_native_dashboard") as? Bool ?? false
1605
+ if useNativeDashboard && dashboardShowing > 0 {
1606
+ return
1607
+ }
1606
1608
 
1607
1609
  if text.isEmpty {
1608
1610
  clearDisplay()
@@ -2191,8 +2193,24 @@ class G2: NSObject, SGCManager {
2191
2193
  return bmp
2192
2194
  }
2193
2195
 
2196
+ /// Bring the Even Realities dashboard (the OS-level home/idle screen) to
2197
+ /// the foreground by tearing down whatever EvenHub page we currently own.
2198
+ /// The glasses fall back to the dashboard automatically when no page is up.
2194
2199
  func showDashboard() {
2195
- // G2 doesn't have a native dashboard concept via EvenHub
2200
+ Bridge.log("G2: showDashboard")
2201
+ dashboardShowing += 2
2202
+ let msg = EvenHubProto.shutdownMessage()
2203
+ sendEvenHubCommand(msg)
2204
+ pageCreated = false
2205
+ pageHasTextContainer = false
2206
+ currentTextContent = ""
2207
+ currentBitmapBase64 = ""
2208
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
2209
+ guard let self = self else { return }
2210
+ // activate the dashboard by setting dept to the current setting:
2211
+ let currentDepth = DeviceStore.shared.get("bluetooth", "dashboard_depth") as? Int ?? 0
2212
+ self.setDashboardDepthOnly(currentDepth)
2213
+ }
2196
2214
  }
2197
2215
 
2198
2216
  func setDashboardPosition(_ height: Int, _ depth: Int) {
@@ -2302,21 +2320,33 @@ class G2: NSObject, SGCManager {
2302
2320
  sendEvenHubCommand(toSend)
2303
2321
  }
2304
2322
 
2323
+ func restartMic() {
2324
+ // if already enabled, set to disabled, then send enabled after 500ms:
2325
+ DeviceStore.shared.apply("glasses", "micEnabled", true)
2326
+ let msg = EvenHubProto.audioControlMessage(enable: false)
2327
+ sendEvenHubCommand(msg)
2328
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
2329
+ guard let self = self else { return }
2330
+ let useNativeDashboard = DeviceStore.shared.get("bluetooth", "use_native_dashboard") as? Bool ?? false
2331
+ Bridge.log("G2: setMicEnabled - useNativeDashboard=\(useNativeDashboard), dashboardShowing=\(dashboardShowing)")
2332
+ if useNativeDashboard && dashboardShowing > 0 {
2333
+ return
2334
+ }
2335
+ if (!pageCreated || !pageHasTextContainer) {
2336
+ DeviceManager.shared.sendCurrentState()// should re-create the page if needed
2337
+ }
2338
+ let msg = EvenHubProto.audioControlMessage(enable: true)
2339
+ self.sendEvenHubCommand(msg)
2340
+ }
2341
+ }
2342
+
2305
2343
  // MARK: - SGCManager: Audio Control
2306
2344
 
2307
2345
  func setMicEnabled(_ enabled: Bool) {
2308
2346
  Bridge.log("G2: setMicEnabled(\(enabled))")
2309
2347
  let currentEnabled = DeviceStore.shared.get("glasses", "micEnabled") as? Bool ?? false
2310
2348
  if currentEnabled && enabled {
2311
- // if already enabled, set to disabled, then send enabled after 500ms:
2312
- DeviceStore.shared.apply("glasses", "micEnabled", true)
2313
- let msg = EvenHubProto.audioControlMessage(enable: false)
2314
- sendEvenHubCommand(msg)
2315
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
2316
- guard let self = self else { return }
2317
- let msg = EvenHubProto.audioControlMessage(enable: true)
2318
- self.sendEvenHubCommand(msg)
2319
- }
2349
+ restartMic()
2320
2350
  return
2321
2351
  }
2322
2352
 
@@ -2505,7 +2535,8 @@ class G2: NSObject, SGCManager {
2505
2535
  // }
2506
2536
 
2507
2537
  // connectController("1B:08:26:8E:0E:E6")
2508
- connectController()
2538
+ // connectController()
2539
+ showDashboard()
2509
2540
  }
2510
2541
 
2511
2542
  func dbg2() {
@@ -2532,6 +2563,8 @@ class G2: NSObject, SGCManager {
2532
2563
  // // update the text
2533
2564
  // Bridge.log("G2: sendTextWall() - updating text container")
2534
2565
  // updateText("test2")
2566
+ let currentDepth = DeviceStore.shared.get("bluetooth", "dashboard_depth") as? Int ?? 0
2567
+ setDashboardDepthOnly(currentDepth)
2535
2568
  }
2536
2569
 
2537
2570
  // MARK: - SGCManager: Device Control
@@ -3021,15 +3054,22 @@ class G2: NSObject, SGCManager {
3021
3054
  if eventType == .doubleClick {
3022
3055
  // trigger dashboard:
3023
3056
  let isHeadUp = DeviceStore.shared.get("glasses", "headUp") as? Bool ?? false
3024
- // toggle head up:
3025
- DeviceStore.shared.apply("glasses", "headUp", !isHeadUp)
3026
- if isHeadUp {
3027
- // Bridge.log("G2: going back to home, clearing display")
3028
- // clear the display after a delay:
3029
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
3030
- self.clearDisplay()
3031
- }
3057
+
3058
+ let useNativeDashboard = DeviceStore.shared.get("bluetooth", "use_native_dashboard") as? Bool ?? false
3059
+ if useNativeDashboard {
3060
+ showDashboard()
3061
+ } else {
3062
+ // toggle head up:
3063
+ DeviceStore.shared.apply("glasses", "headUp", !isHeadUp)
3032
3064
  }
3065
+
3066
+ // if isHeadUp {
3067
+ // // Bridge.log("G2: going back to home, clearing display")
3068
+ // // clear the display after a delay:
3069
+ // DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
3070
+ // self.clearDisplay()
3071
+ // }
3072
+ // }
3033
3073
  // sendDashboardCommand(DashboardCommand.trigger)
3034
3074
 
3035
3075
  // toggle head up:
@@ -3373,18 +3413,39 @@ class G2: NSObject, SGCManager {
3373
3413
  // if we got 08011A00 that means we closed the dashboard, which means the mic is probably dead,
3374
3414
  // so we need to revive it:
3375
3415
  if data == Data([0x08, 0x01, 0x1A, 0x00]) {
3376
- Bridge.log("G2: gesture_ctrl response: dashboard closed")
3377
- // re-send mic on / update mic state:
3378
- DeviceStore.shared.apply("glasses", "micEnabled", false)
3379
- DeviceManager.shared.updateMicState() // should set the mic back on if it should be on
3380
- // // let isHeadUp = DeviceStore.shared.get("glasses", "headUp") as? Bool ?? false
3381
-
3382
- // // toggle head up:
3383
- // DeviceStore.shared.apply("glasses", "headUp", false)
3384
- // // send the current state to the glasses
3385
- // DeviceManager.shared.sendCurrentState()
3386
- // reset the text container (different from clearDisplay())
3387
- sendTextWall(" ")
3416
+ Bridge.log("G2: dashboard closed / shutdown - dashboardShowing=\(dashboardShowing)")
3417
+ let useNativeDashboard = DeviceStore.shared.get("bluetooth", "use_native_dashboard") as? Bool ?? false
3418
+ if !useNativeDashboard {
3419
+ // make sure the container exists:
3420
+ DeviceManager.shared.sendCurrentState()
3421
+ // re-send mic on / if it's enabled:
3422
+ let micEnabled = DeviceStore.shared.get("glasses", "micEnabled") as? Bool ?? false
3423
+ if micEnabled {
3424
+ restartMic()
3425
+ }
3426
+ // reset the text container (different from clearDisplay())
3427
+ // sendTextWall(" ")
3428
+ // createPageWithText(" ")
3429
+ } else {
3430
+ // if we aren't trying to show the dashboard
3431
+ // then we need to turn the mic back on and display the mentra main page:
3432
+ if dashboardShowing <= 1 {
3433
+ dashboardShowing = 0
3434
+ // make sure the container exists:
3435
+ DeviceManager.shared.sendCurrentState()
3436
+ // set the mic back on if it should be on
3437
+ let micEnabled = DeviceStore.shared.get("glasses", "micEnabled") as? Bool ?? false
3438
+ if micEnabled {
3439
+ restartMic()
3440
+ }
3441
+ return
3442
+ }
3443
+ // do nothing this time since we just closed the dashboard
3444
+ dashboardShowing -= 1
3445
+ if (dashboardShowing < 0) {
3446
+ dashboardShowing = 0
3447
+ }
3448
+ }
3388
3449
  }
3389
3450
 
3390
3451
  // if we got 08011097012200 that means we selected a menu item:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/bluetooth-sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "SDK for communicating with smart glasses",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -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 legacy store mapping. */
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,10 +718,20 @@ 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
731
+ /**
732
+ * Optional scan signal strength. It may be undefined at first discovery and
733
+ * appear in a later scan update when the platform reports RSSI metadata.
734
+ */
718
735
  rssi?: number
719
736
  }
720
737
 
package/src/_internal.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * MentraOS-only compatibility entrypoint.
3
3
  *
4
- * Partner apps should import from `@mentra/bluetooth-sdk`. This file is not a
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<BluetoothStatus>) => void
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<BluetoothStatus>
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<BluetoothStatus>, model: DeviceModel): Device[] {
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 ?? false,
393
+ bypassVad ?? true,
392
394
  sendTranscript ?? false,
393
395
  sendLc3Data ?? false,
394
396
  ),
@@ -464,6 +466,9 @@ NativeBluetoothSdkModule.scan = async function (
464
466
  }
465
467
 
466
468
  const handleBluetoothStatus = (status: Partial<BluetoothStatus>) => {
469
+ if (!Array.isArray(status.searchResults)) {
470
+ return
471
+ }
467
472
  emitResults(searchResultsForModel(status, scanOptions.model))
468
473
  }
469
474
 
package/src/index.ts CHANGED
@@ -1,10 +1,101 @@
1
- import {BluetoothSdk} from "./_private/BluetoothSdkModule"
1
+ import PrivateBluetoothSdkModule from "./_private/BluetoothSdkModule"
2
+ import type {
3
+ BluetoothSdkEventListener,
4
+ BluetoothSdkEventName,
5
+ BluetoothSdkPublicModule,
6
+ } from "./BluetoothSdk.types"
7
+
8
+ const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
9
+ "log",
10
+ "device_discovered",
11
+ "default_device_changed",
12
+ "glasses_not_ready",
13
+ "button_press",
14
+ "touch_event",
15
+ "head_up",
16
+ "vad_status",
17
+ "battery_status",
18
+ "local_transcription",
19
+ "wifi_status_change",
20
+ "hotspot_status_change",
21
+ "hotspot_error",
22
+ "photo_response",
23
+ "gallery_status",
24
+ "compatible_glasses_search_stop",
25
+ "swipe_volume_status",
26
+ "switch_status",
27
+ "rgb_led_control_response",
28
+ "pair_failure",
29
+ "audio_pairing_needed",
30
+ "audio_connected",
31
+ "audio_disconnected",
32
+ "mic_pcm",
33
+ "mic_lc3",
34
+ "stream_status",
35
+ "keep_alive_ack",
36
+ ])
37
+
38
+ const addListener: BluetoothSdkPublicModule["addListener"] = (eventName, listener) => {
39
+ if (!PUBLIC_EVENT_NAMES.has(eventName)) {
40
+ throw new Error(
41
+ `Unsupported BluetoothSdk event "${eventName}". Use @mentra/bluetooth-sdk/react for status state.`,
42
+ )
43
+ }
44
+ return PrivateBluetoothSdkModule.addListener(
45
+ eventName,
46
+ listener as BluetoothSdkEventListener<BluetoothSdkEventName>,
47
+ )
48
+ }
49
+
50
+ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
51
+ addListener,
52
+ getDefaultDevice: PrivateBluetoothSdkModule.getDefaultDevice.bind(PrivateBluetoothSdkModule),
53
+ setDefaultDevice: PrivateBluetoothSdkModule.setDefaultDevice.bind(PrivateBluetoothSdkModule),
54
+ clearDefaultDevice: PrivateBluetoothSdkModule.clearDefaultDevice.bind(PrivateBluetoothSdkModule),
55
+ startScan: PrivateBluetoothSdkModule.startScan.bind(PrivateBluetoothSdkModule),
56
+ stopScan: PrivateBluetoothSdkModule.stopScan.bind(PrivateBluetoothSdkModule),
57
+ scan: PrivateBluetoothSdkModule.scan.bind(PrivateBluetoothSdkModule) as BluetoothSdkPublicModule["scan"],
58
+ connect: PrivateBluetoothSdkModule.connect.bind(PrivateBluetoothSdkModule),
59
+ connectDefault: PrivateBluetoothSdkModule.connectDefault.bind(PrivateBluetoothSdkModule),
60
+ cancelConnectionAttempt: PrivateBluetoothSdkModule.cancelConnectionAttempt.bind(PrivateBluetoothSdkModule),
61
+ disconnect: PrivateBluetoothSdkModule.disconnect.bind(PrivateBluetoothSdkModule),
62
+ forget: PrivateBluetoothSdkModule.forget.bind(PrivateBluetoothSdkModule),
63
+ displayText: PrivateBluetoothSdkModule.displayText.bind(PrivateBluetoothSdkModule),
64
+ clearDisplay: PrivateBluetoothSdkModule.clearDisplay.bind(PrivateBluetoothSdkModule),
65
+ showDashboard: PrivateBluetoothSdkModule.showDashboard.bind(PrivateBluetoothSdkModule),
66
+ setDashboardPosition: PrivateBluetoothSdkModule.setDashboardPosition.bind(PrivateBluetoothSdkModule),
67
+ setHeadUpAngle: PrivateBluetoothSdkModule.setHeadUpAngle.bind(PrivateBluetoothSdkModule),
68
+ setScreenDisabled: PrivateBluetoothSdkModule.setScreenDisabled.bind(PrivateBluetoothSdkModule),
69
+ requestWifiScan: PrivateBluetoothSdkModule.requestWifiScan.bind(PrivateBluetoothSdkModule),
70
+ sendWifiCredentials: PrivateBluetoothSdkModule.sendWifiCredentials.bind(PrivateBluetoothSdkModule),
71
+ forgetWifiNetwork: PrivateBluetoothSdkModule.forgetWifiNetwork.bind(PrivateBluetoothSdkModule),
72
+ setHotspotState: PrivateBluetoothSdkModule.setHotspotState.bind(PrivateBluetoothSdkModule),
73
+ setGalleryMode: PrivateBluetoothSdkModule.setGalleryMode.bind(PrivateBluetoothSdkModule),
74
+ setButtonPhotoSettings: PrivateBluetoothSdkModule.setButtonPhotoSettings.bind(PrivateBluetoothSdkModule),
75
+ setButtonVideoRecordingSettings: PrivateBluetoothSdkModule.setButtonVideoRecordingSettings.bind(PrivateBluetoothSdkModule),
76
+ setButtonCameraLed: PrivateBluetoothSdkModule.setButtonCameraLed.bind(PrivateBluetoothSdkModule),
77
+ setButtonMaxRecordingTime: PrivateBluetoothSdkModule.setButtonMaxRecordingTime.bind(PrivateBluetoothSdkModule),
78
+ setCameraFov: PrivateBluetoothSdkModule.setCameraFov.bind(PrivateBluetoothSdkModule),
79
+ queryGalleryStatus: PrivateBluetoothSdkModule.queryGalleryStatus.bind(PrivateBluetoothSdkModule),
80
+ requestPhoto: PrivateBluetoothSdkModule.requestPhoto.bind(PrivateBluetoothSdkModule),
81
+ startVideoRecording: PrivateBluetoothSdkModule.startVideoRecording.bind(PrivateBluetoothSdkModule),
82
+ stopVideoRecording: PrivateBluetoothSdkModule.stopVideoRecording.bind(PrivateBluetoothSdkModule),
83
+ startStream: PrivateBluetoothSdkModule.startStream.bind(PrivateBluetoothSdkModule),
84
+ stopStream: PrivateBluetoothSdkModule.stopStream.bind(PrivateBluetoothSdkModule),
85
+ keepStreamAlive: PrivateBluetoothSdkModule.keepStreamAlive.bind(PrivateBluetoothSdkModule),
86
+ setMicState: PrivateBluetoothSdkModule.setMicState.bind(PrivateBluetoothSdkModule),
87
+ setPreferredMic: PrivateBluetoothSdkModule.setPreferredMic.bind(PrivateBluetoothSdkModule),
88
+ setOwnAppAudioPlaying: PrivateBluetoothSdkModule.setOwnAppAudioPlaying.bind(PrivateBluetoothSdkModule),
89
+ getGlassesMediaVolume: PrivateBluetoothSdkModule.getGlassesMediaVolume.bind(PrivateBluetoothSdkModule),
90
+ setGlassesMediaVolume: PrivateBluetoothSdkModule.setGlassesMediaVolume.bind(PrivateBluetoothSdkModule),
91
+ rgbLedControl: PrivateBluetoothSdkModule.rgbLedControl.bind(PrivateBluetoothSdkModule),
92
+ requestVersionInfo: PrivateBluetoothSdkModule.requestVersionInfo.bind(PrivateBluetoothSdkModule),
93
+ })
2
94
 
3
95
  export default BluetoothSdk
4
96
 
5
97
  export {
6
98
  DeviceModels,
7
- createDisconnectedGlassesStatus,
8
99
  isBusyGlassesConnectionStatus,
9
100
  isConnectedGlassesConnectionStatus,
10
101
  isConnectedWifiStatus,
@@ -54,8 +145,6 @@ export type {
54
145
  PhotoCompression,
55
146
  PhotoResponseEvent,
56
147
  PhotoSize,
57
- PublicBluetoothStatus as BluetoothStatus,
58
- PublicGlassesStatus as GlassesStatus,
59
148
  RgbLedAction,
60
149
  RgbLedColor,
61
150
  RgbLedControlResponseEvent,
@@ -8,15 +8,17 @@ export {
8
8
  type BluetoothScanHookResult,
9
9
  type UseBluetoothScanOptions,
10
10
  } from "./useBluetoothScan"
11
+ export type {DefaultDeviceStorage} from "./useGlassesConnection"
11
12
  export {
12
- useBluetoothStatus,
13
- type BluetoothStatusHookResult,
14
- type UseBluetoothStatusOptions,
15
- } from "./useBluetoothStatus"
16
- export {
17
- useGlassesConnection,
18
- type DefaultDeviceStorage,
19
- type GlassesConnectionAction,
20
- type GlassesConnectionHookResult,
21
- type UseGlassesConnectionOptions,
22
- } from "./useGlassesConnection"
13
+ useMentraBluetooth,
14
+ type BatteryState,
15
+ type ConnectedGlassesInfo,
16
+ type FirmwareInfo,
17
+ type GalleryModeState,
18
+ type GlassesRuntimeState,
19
+ type MentraBluetoothSession,
20
+ type PhoneSdkRuntimeState,
21
+ type ScanController,
22
+ type SignalState,
23
+ type UseMentraBluetoothOptions,
24
+ } from "./useMentraBluetooth"
@@ -1,6 +1,6 @@
1
1
  import {useEffect, useRef, useState} from "react"
2
2
 
3
- import BluetoothSdk from "../index"
3
+ import BluetoothSdk from "../_private/BluetoothSdkModule"
4
4
  import {
5
5
  createDisconnectedGlassesStatus,
6
6
  isConnectedGlassesConnectionStatus,
@@ -96,6 +96,21 @@ export function useGlassesConnection(
96
96
  }
97
97
  }, [])
98
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
+
99
114
  useEffect(() => {
100
115
  if (!options.autoConnectDefault || autoConnectAttemptedRef.current || status.connected || !defaultDevice) {
101
116
  return