@mentra/bluetooth-sdk 0.1.0 → 0.1.2

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.
@@ -1,15 +1,23 @@
1
1
  import {NativeModule, requireNativeModule} from "expo"
2
2
 
3
3
  import {
4
- ConnectOptions,
4
+ BluetoothSettingsUpdate,
5
5
  BluetoothSdkModuleEvents,
6
6
  BluetoothStatus,
7
- DeviceScanRequest,
8
- GalleryMode,
7
+ ButtonPhotoSize,
8
+ CameraFov,
9
+ CameraFovSetting,
10
+ ConnectFirstOptions,
11
+ ConnectOptions,
12
+ DashboardMenuItem,
9
13
  Device,
14
+ DeviceModel,
15
+ GalleryMode,
10
16
  GlassesMediaVolumeGetResult,
11
17
  GlassesMediaVolumeSetResult,
12
18
  GlassesStatus,
19
+ MicPreference,
20
+ ObservableStoreCategory,
13
21
  PhotoCompression,
14
22
  PhotoSize,
15
23
  RgbLedAction,
@@ -27,11 +35,11 @@ declare class BluetoothSdkModule extends NativeModule<BluetoothSdkModuleEvents>
27
35
  getGlassesStatus(): Promise<GlassesStatus>
28
36
  getBluetoothStatus(): Promise<BluetoothStatus>
29
37
  getDefaultDevice(): Promise<Device | null>
30
- update(category: string, values: Record<string, any>): Promise<void>
38
+ update(category: ObservableStoreCategory, values: object): Promise<void>
31
39
 
32
40
  // Display Commands
33
- displayEvent(params: Record<string, any>): Promise<void>
34
- displayText(params: Record<string, any>): Promise<void>
41
+ displayEvent(params: Record<string, unknown>): Promise<void>
42
+ displayText(text: string, x?: number, y?: number, size?: number): Promise<void>
35
43
  clearDisplay(): Promise<void>
36
44
 
37
45
  // Connection Commands
@@ -40,7 +48,8 @@ declare class BluetoothSdkModule extends NativeModule<BluetoothSdkModuleEvents>
40
48
  connectDefaultWithOptions(options: Required<ConnectOptions>): Promise<void>
41
49
  setDefaultDevice(device: Device | null): Promise<void>
42
50
  clearDefaultDevice(): Promise<void>
43
- startScan(params: DeviceScanRequest): Promise<void>
51
+ startScan(model: DeviceModel): Promise<void>
52
+ connectFirst(model: DeviceModel, options?: ConnectFirstOptions): Promise<Device>
44
53
  connect(device: Device, options?: ConnectOptions): Promise<void>
45
54
  connectWithOptions(device: Device, options: Required<ConnectOptions>): Promise<void>
46
55
  cancelConnectionAttempt(): Promise<void>
@@ -51,6 +60,12 @@ declare class BluetoothSdkModule extends NativeModule<BluetoothSdkModuleEvents>
51
60
  forget(): Promise<void>
52
61
  forgetController(): Promise<void>
53
62
  showDashboard(): Promise<void>
63
+ setBrightness(level: number, autoMode?: boolean | null): Promise<void>
64
+ setAutoBrightness(enabled: boolean): Promise<void>
65
+ setDashboardPosition(height: number, depth: number): Promise<void>
66
+ setDashboardMenu(items: DashboardMenuItem[]): Promise<void>
67
+ setHeadUpAngle(angleDegrees: number): Promise<void>
68
+ setScreenDisabled(disabled: boolean): Promise<void>
54
69
  ping(): Promise<void>
55
70
  dbg1(): Promise<void>
56
71
  dbg2(): Promise<void>
@@ -68,6 +83,11 @@ declare class BluetoothSdkModule extends NativeModule<BluetoothSdkModuleEvents>
68
83
 
69
84
  // Gallery Commands
70
85
  setGalleryMode(mode: GalleryMode): Promise<void>
86
+ setButtonPhotoSettings(size: ButtonPhotoSize): Promise<void>
87
+ setButtonVideoRecordingSettings(width: number, height: number, fps: number): Promise<void>
88
+ setButtonCameraLed(enabled: boolean): Promise<void>
89
+ setButtonMaxRecordingTime(minutes: number): Promise<void>
90
+ setCameraFov(fov: CameraFov): Promise<void>
71
91
  queryGalleryStatus(): Promise<void>
72
92
  photoRequest(
73
93
  requestId: string,
@@ -97,7 +117,14 @@ declare class BluetoothSdkModule extends NativeModule<BluetoothSdkModuleEvents>
97
117
  keepStreamAlive(params: StreamKeepAliveRequest): Promise<void>
98
118
 
99
119
  // Microphone Commands
100
- setMicState(sendPcmData: boolean, sendTranscript: boolean, bypassVad: boolean): Promise<void>
120
+ setMicState(
121
+ enabled: boolean,
122
+ useGlassesMic?: boolean,
123
+ bypassVad?: boolean,
124
+ sendTranscript?: boolean,
125
+ sendLc3Data?: boolean,
126
+ ): Promise<void>
127
+ setPreferredMic(preferredMic: MicPreference): Promise<void>
101
128
  restartTranscriber(): Promise<void>
102
129
 
103
130
  // Audio Playback Monitoring
@@ -130,7 +157,7 @@ declare class BluetoothSdkModule extends NativeModule<BluetoothSdkModuleEvents>
130
157
 
131
158
  // Helper methods for type-safe observable store access
132
159
  updateGlasses(values: Partial<GlassesStatus>): Promise<void>
133
- updateBluetoothSettings(values: Record<string, any>): Promise<void>
160
+ updateBluetoothSettings(values: BluetoothSettingsUpdate): Promise<void>
134
161
  onGlassesStatus(callback: GlassesListener): () => void
135
162
  onBluetoothStatus(callback: BluetoothStatusListener): () => void
136
163
 
@@ -147,7 +174,26 @@ const DEFAULT_CONNECT_OPTIONS: Required<ConnectOptions> = {
147
174
  cancelExistingConnectionAttempt: true,
148
175
  }
149
176
 
150
- function adaptConnectionStatusToNative(connection: GlassesStatus["connection"]): Record<string, any> {
177
+ const DEFAULT_CONNECT_FIRST_TIMEOUT_MS = 15_000
178
+
179
+ const CAMERA_FOV_SETTINGS: Record<CameraFov, CameraFovSetting> = {
180
+ standard: {fov: 118, roi_position: 0},
181
+ wide: {fov: 118, roi_position: 0},
182
+ }
183
+
184
+ function findSearchResult(status: Partial<BluetoothStatus>, model: DeviceModel): Device | null {
185
+ return status.searchResults?.find((device) => device.model === model) ?? null
186
+ }
187
+
188
+ function dashboardMenuItemToNative(item: DashboardMenuItem): Record<string, unknown> {
189
+ return {
190
+ ...(item.values ?? {}),
191
+ title: item.title,
192
+ packageName: item.packageName,
193
+ }
194
+ }
195
+
196
+ function adaptConnectionStatusToNative(connection: GlassesStatus["connection"]): Record<string, unknown> {
151
197
  switch (connection.state) {
152
198
  case "connected":
153
199
  return {connectionState: "CONNECTED", connected: true, fullyBooted: connection.fullyBooted}
@@ -162,9 +208,9 @@ function adaptConnectionStatusToNative(connection: GlassesStatus["connection"]):
162
208
  }
163
209
  }
164
210
 
165
- function adaptGlassesUpdateToNative(values: Partial<GlassesStatus>): Record<string, any> {
211
+ function adaptGlassesUpdateToNative(values: Partial<GlassesStatus>): Record<string, unknown> {
166
212
  const {wifi, hotspot, connection, ...rest} = values
167
- let update: Record<string, any> = {...rest}
213
+ let update: Record<string, unknown> = {...rest}
168
214
  if (connection) {
169
215
  update = {
170
216
  ...update,
@@ -226,14 +272,109 @@ NativeBluetoothSdkModule.getDefaultDevice = function () {
226
272
  return Promise.resolve(nativeGetDefaultDevice())
227
273
  }
228
274
 
275
+ const nativeSetMicState = NativeBluetoothSdkModule.setMicState.bind(NativeBluetoothSdkModule) as (
276
+ enabled: boolean,
277
+ useGlassesMic: boolean,
278
+ bypassVad: boolean,
279
+ sendTranscript: boolean,
280
+ sendLc3Data: boolean,
281
+ ) => MaybePromise<void>
282
+
283
+ const nativeDisplayText = NativeBluetoothSdkModule.displayText.bind(NativeBluetoothSdkModule) as (
284
+ text: string,
285
+ x: number,
286
+ y: number,
287
+ size: number,
288
+ ) => MaybePromise<void>
289
+
229
290
  NativeBluetoothSdkModule.updateGlasses = function (values: Partial<GlassesStatus>) {
230
291
  return this.update("glasses", adaptGlassesUpdateToNative(values))
231
292
  }
232
293
 
233
- NativeBluetoothSdkModule.updateBluetoothSettings = function (values: Record<string, any>) {
294
+ NativeBluetoothSdkModule.updateBluetoothSettings = function (values: BluetoothSettingsUpdate) {
234
295
  return this.update("bluetooth", values)
235
296
  }
236
297
 
298
+ NativeBluetoothSdkModule.displayText = function (text: string, x?: number, y?: number, size?: number) {
299
+ return Promise.resolve(nativeDisplayText(text, x ?? 0, y ?? 0, size ?? 24))
300
+ }
301
+
302
+ NativeBluetoothSdkModule.setBrightness = function (level: number, autoMode?: boolean | null) {
303
+ return this.updateBluetoothSettings({
304
+ ...(autoMode == null ? {} : {auto_brightness: autoMode}),
305
+ brightness: level,
306
+ })
307
+ }
308
+
309
+ NativeBluetoothSdkModule.setAutoBrightness = function (enabled: boolean) {
310
+ return this.updateBluetoothSettings({auto_brightness: enabled})
311
+ }
312
+
313
+ NativeBluetoothSdkModule.setDashboardPosition = function (height: number, depth: number) {
314
+ return this.updateBluetoothSettings({
315
+ dashboard_height: height,
316
+ dashboard_depth: depth,
317
+ })
318
+ }
319
+
320
+ NativeBluetoothSdkModule.setDashboardMenu = function (items: DashboardMenuItem[]) {
321
+ return this.updateBluetoothSettings({menu_apps: items.map(dashboardMenuItemToNative)})
322
+ }
323
+
324
+ NativeBluetoothSdkModule.setHeadUpAngle = function (angleDegrees: number) {
325
+ return this.updateBluetoothSettings({head_up_angle: angleDegrees})
326
+ }
327
+
328
+ NativeBluetoothSdkModule.setScreenDisabled = function (disabled: boolean) {
329
+ return this.updateBluetoothSettings({screen_disabled: disabled})
330
+ }
331
+
332
+ NativeBluetoothSdkModule.setButtonPhotoSettings = function (size: ButtonPhotoSize) {
333
+ return this.updateBluetoothSettings({button_photo_size: size})
334
+ }
335
+
336
+ NativeBluetoothSdkModule.setButtonVideoRecordingSettings = function (width: number, height: number, fps: number) {
337
+ return this.updateBluetoothSettings({
338
+ button_video_width: width,
339
+ button_video_height: height,
340
+ button_video_fps: fps,
341
+ })
342
+ }
343
+
344
+ NativeBluetoothSdkModule.setButtonCameraLed = function (enabled: boolean) {
345
+ return this.updateBluetoothSettings({button_camera_led: enabled})
346
+ }
347
+
348
+ NativeBluetoothSdkModule.setButtonMaxRecordingTime = function (minutes: number) {
349
+ return this.updateBluetoothSettings({button_max_recording_time: minutes})
350
+ }
351
+
352
+ NativeBluetoothSdkModule.setCameraFov = function (fov: CameraFov) {
353
+ return this.updateBluetoothSettings({camera_fov: CAMERA_FOV_SETTINGS[fov]})
354
+ }
355
+
356
+ NativeBluetoothSdkModule.setMicState = function (
357
+ enabled: boolean,
358
+ useGlassesMic?: boolean,
359
+ bypassVad?: boolean,
360
+ sendTranscript?: boolean,
361
+ sendLc3Data?: boolean,
362
+ ) {
363
+ return Promise.resolve(
364
+ nativeSetMicState(
365
+ enabled,
366
+ useGlassesMic ?? true,
367
+ bypassVad ?? false,
368
+ sendTranscript ?? false,
369
+ sendLc3Data ?? false,
370
+ ),
371
+ )
372
+ }
373
+
374
+ NativeBluetoothSdkModule.setPreferredMic = function (preferredMic: MicPreference) {
375
+ return this.updateBluetoothSettings({preferred_mic: preferredMic})
376
+ }
377
+
237
378
  NativeBluetoothSdkModule.onGlassesStatus = function (callback: GlassesListener) {
238
379
  const subscription = this.addListener("glasses_status", callback)
239
380
  return () => subscription.remove()
@@ -256,4 +397,57 @@ NativeBluetoothSdkModule.connect = function (device: Device, options?: ConnectOp
256
397
  return this.connectWithOptions(device, {...DEFAULT_CONNECT_OPTIONS, ...options})
257
398
  }
258
399
 
400
+ NativeBluetoothSdkModule.connectFirst = async function (model: DeviceModel, options?: ConnectFirstOptions) {
401
+ const {timeoutMs = DEFAULT_CONNECT_FIRST_TIMEOUT_MS, ...connectOptions} = options ?? {}
402
+
403
+ const device = await new Promise<Device>((resolve, reject) => {
404
+ let timeout: ReturnType<typeof setTimeout> | null = null
405
+ let removeBluetoothListener = () => {}
406
+ let settled = false
407
+
408
+ const cleanup = () => {
409
+ if (timeout) {
410
+ clearTimeout(timeout)
411
+ }
412
+ removeBluetoothListener()
413
+ }
414
+
415
+ const settle = (error: Error | null, result?: Device) => {
416
+ if (settled) {
417
+ return
418
+ }
419
+ settled = true
420
+ cleanup()
421
+ if (error) {
422
+ reject(error)
423
+ } else if (result) {
424
+ resolve(result)
425
+ }
426
+ }
427
+
428
+ const handleBluetoothStatus = (status: Partial<BluetoothStatus>) => {
429
+ const result = findSearchResult(status, model)
430
+ if (result) {
431
+ settle(null, result)
432
+ }
433
+ }
434
+
435
+ removeBluetoothListener = this.onBluetoothStatus(handleBluetoothStatus)
436
+
437
+ if (Number.isFinite(timeoutMs) && timeoutMs > 0) {
438
+ timeout = setTimeout(() => {
439
+ settle(new Error(`Timed out after ${timeoutMs}ms while scanning for ${model}.`))
440
+ }, timeoutMs)
441
+ }
442
+
443
+ Promise.resolve(this.startScan(model))
444
+ .then(() => this.getBluetoothStatus())
445
+ .then(handleBluetoothStatus)
446
+ .catch((error) => settle(error instanceof Error ? error : new Error(String(error))))
447
+ })
448
+
449
+ await this.connect(device, connectOptions)
450
+ return device
451
+ }
452
+
259
453
  export default NativeBluetoothSdkModule