@mentra/bluetooth-sdk 3.1.0-dev.7 → 3.1.0-dev.71

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 (51) hide show
  1. package/README.md +40 -34
  2. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +0 -5
  3. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +13 -8
  4. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +7 -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 +15 -0
  7. package/android/src/main/java/com/mentra/bluetoothsdk/ReleaseChangelog.kt +61 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +81 -69
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
  10. package/android/src/test/java/com/mentra/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
  11. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
  12. package/build/BluetoothSdk.types.d.ts +26 -5
  13. package/build/BluetoothSdk.types.d.ts.map +1 -1
  14. package/build/BluetoothSdk.types.js.map +1 -1
  15. package/build/changelogs.d.ts +7 -0
  16. package/build/changelogs.d.ts.map +1 -0
  17. package/build/changelogs.js +44 -0
  18. package/build/changelogs.js.map +1 -0
  19. package/build/generated/changelogCatalog.d.ts +6 -0
  20. package/build/generated/changelogCatalog.d.ts.map +1 -0
  21. package/build/generated/changelogCatalog.js +8 -0
  22. package/build/generated/changelogCatalog.js.map +1 -0
  23. package/build/generated/releaseMetadata.js +5 -5
  24. package/build/generated/releaseMetadata.js.map +1 -1
  25. package/build/index.d.ts +1 -1
  26. package/build/index.d.ts.map +1 -1
  27. package/build/index.js +14 -0
  28. package/build/index.js.map +1 -1
  29. package/build/ota-transport/index.d.ts +67 -0
  30. package/build/ota-transport/index.d.ts.map +1 -0
  31. package/build/ota-transport/index.js +58 -0
  32. package/build/ota-transport/index.js.map +1 -0
  33. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  34. package/ios/Source/Bridge.swift +1 -6
  35. package/ios/Source/DeviceManager.swift +13 -8
  36. package/ios/Source/GeneratedChangelogCatalog.swift +6 -0
  37. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  38. package/ios/Source/MentraBluetoothSDK.swift +17 -0
  39. package/ios/Source/ReleaseChangelog.swift +65 -0
  40. package/ios/Source/sgcs/MentraLive.swift +93 -80
  41. package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
  42. package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
  43. package/ios/Tests/ReleaseChangelogTests.swift +13 -0
  44. package/package.json +6 -1
  45. package/scripts/public-ota-api.test.mjs +37 -0
  46. package/src/BluetoothSdk.types.ts +28 -5
  47. package/src/changelogs.ts +44 -0
  48. package/src/generated/changelogCatalog.ts +9 -0
  49. package/src/generated/releaseMetadata.ts +5 -5
  50. package/src/index.ts +24 -0
  51. package/src/ota-transport/index.ts +116 -0
@@ -0,0 +1,44 @@
1
+ import type {ReleaseChangelog} from "./BluetoothSdk.types"
2
+ import {GENERATED_RELEASE_CHANGELOGS} from "./generated/changelogCatalog"
3
+ import {BLUETOOTH_SDK_RELEASE_METADATA} from "./generated/releaseMetadata"
4
+
5
+ const BASE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+].*)?$/
6
+
7
+ function baseVersion(version: string, label: string): string {
8
+ const match = BASE_VERSION_PATTERN.exec(version.trim())
9
+ if (!match) throw new Error(`${label} must be a semantic version such as 3.1.0, 3.1.0-dev.4, or 3.1.0-beta.2`)
10
+ return `${match[1]}.${match[2]}.${match[3]}`
11
+ }
12
+
13
+ function compareVersions(left: string, right: string): number {
14
+ const a = left.split(".").map(Number)
15
+ const b = right.split(".").map(Number)
16
+ for (let index = 0; index < 3; index += 1) {
17
+ if (a[index] !== b[index]) return a[index] - b[index]
18
+ }
19
+ return 0
20
+ }
21
+
22
+ /**
23
+ * Return authored release notes crossed between two product versions, newest first.
24
+ * The target release is always included, including transitions within one base train.
25
+ */
26
+ export function getReleaseChangelogs(fromVersion?: string | null, toVersion?: string | null): ReleaseChangelog[] {
27
+ const fallbackTarget = BLUETOOTH_SDK_RELEASE_METADATA.familyBaseVersion ?? GENERATED_RELEASE_CHANGELOGS[0]?.version
28
+ if (!fallbackTarget && !toVersion) return []
29
+ const target = baseVersion(toVersion ?? fallbackTarget, "toVersion")
30
+ if (!GENERATED_RELEASE_CHANGELOGS.some(({version}) => version === target)) {
31
+ throw new Error(`No changelog is bundled for target version ${target}`)
32
+ }
33
+ if (!fromVersion) {
34
+ return GENERATED_RELEASE_CHANGELOGS.filter(({version}) => version === target).map((entry) => ({...entry}))
35
+ }
36
+
37
+ const source = baseVersion(fromVersion, "fromVersion")
38
+ const direction = compareVersions(target, source)
39
+ return GENERATED_RELEASE_CHANGELOGS.filter(({version}) => {
40
+ if (direction === 0) return version === target
41
+ if (direction > 0) return compareVersions(version, source) > 0 && compareVersions(version, target) <= 0
42
+ return compareVersions(version, source) < 0 && compareVersions(version, target) >= 0
43
+ }).map((entry) => ({...entry}))
44
+ }
@@ -0,0 +1,9 @@
1
+ /** Generated from /changelogs. Do not edit directly. */
2
+ export const GENERATED_RELEASE_CHANGELOGS = Object.freeze(
3
+ [
4
+ {
5
+ "version": "3.1.0",
6
+ "markdown": "Software updates are more reliable, with clearer progress and recovery when the glasses restart.\n\n- MentraOS, Mentra Engine, the Bluetooth SDK, and the glasses client now share one coordinated release version.\n- Mentra Live updates can continue across APK, system, and firmware restarts without asking the user to start the same update again.\n- Bluetooth photo capture and transfer diagnostics are more precise and less disruptive to normal glasses traffic."
7
+ }
8
+ ] as const,
9
+ )
@@ -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.1.0",
15
- "releaseIdentity": "3.1.0-dev.7",
16
- "releaseSetId": "mentra-3.1.0-dev.7",
17
- "sourceCommit": "872697a6395c31d30ec75d64f039eaa763dcbb51",
18
- "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-v3.1.0-dev.7/mentra-live-ota-3.1.0-dev.7.json",
19
- "otaManifestSha256": "8fbea7589345658dc5dffcb394229ffc5c52fbe75352acb8f6827d83290ce750"
15
+ "releaseIdentity": "3.1.0-dev.71",
16
+ "releaseSetId": "mentra-3.1.0-dev.71",
17
+ "sourceCommit": "314b41c83242c63409504b8ca733d74f022eb7d5",
18
+ "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.1.0/mentra-live-ota-3.1.0-dev.71.json",
19
+ "otaManifestSha256": "71904f40a2b648de0d626d05249be71baf8faffc27ad0bcb120d61001ed7d735"
20
20
  })
package/src/index.ts CHANGED
@@ -3,8 +3,11 @@ import type {
3
3
  BluetoothSdkEventListener,
4
4
  BluetoothSdkEventName,
5
5
  BluetoothSdkPublicModule,
6
+ PublicBluetoothStatus,
7
+ PublicGlassesStatus,
6
8
  VideoRecordingDefaults,
7
9
  } from "./BluetoothSdk.types"
10
+ import {getReleaseChangelogs} from "./changelogs"
8
11
 
9
12
  const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
10
13
  "log",
@@ -46,6 +49,8 @@ const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
46
49
  "mic_lc3",
47
50
  "mic_health",
48
51
  "stream_status",
52
+ "mtk_update_complete",
53
+ "glasses_session_changed",
49
54
  "ota_start_ack",
50
55
  "ota_status",
51
56
  "ar99_ota_status",
@@ -64,6 +69,10 @@ const startOtaUpdate: BluetoothSdkPublicModule["startOtaUpdate"] = (otaVersionUr
64
69
  return PrivateBluetoothSdkModule.startOtaUpdate(otaVersionUrl)
65
70
  }
66
71
 
72
+ const queryOtaStatus: BluetoothSdkPublicModule["queryOtaStatus"] = () => {
73
+ return PrivateBluetoothSdkModule.sendOtaQueryStatus()
74
+ }
75
+
67
76
  const bindPublicMethod = <K extends keyof BluetoothSdkPublicModule>(name: K): BluetoothSdkPublicModule[K] => {
68
77
  const method = (PrivateBluetoothSdkModule as unknown as Record<string, unknown>)[name]
69
78
  if (typeof method === "function") {
@@ -77,6 +86,12 @@ const bindPublicMethod = <K extends keyof BluetoothSdkPublicModule>(name: K): Bl
77
86
 
78
87
  export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
79
88
  addListener,
89
+ getGlassesStatus: bindPublicMethod("getGlassesStatus"),
90
+ getBluetoothStatus: bindPublicMethod("getBluetoothStatus"),
91
+ subscribeGlassesStatus: (listener: (changed: Partial<PublicGlassesStatus>) => void) =>
92
+ PrivateBluetoothSdkModule.onGlassesStatus(listener),
93
+ subscribeBluetoothStatus: (listener: (changed: Partial<PublicBluetoothStatus>) => void) =>
94
+ PrivateBluetoothSdkModule.onBluetoothStatus(listener),
80
95
  getDefaultDevice: bindPublicMethod("getDefaultDevice"),
81
96
  setDefaultDevice: bindPublicMethod("setDefaultDevice"),
82
97
  clearDefaultDevice: bindPublicMethod("clearDefaultDevice"),
@@ -95,10 +110,12 @@ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
95
110
  setHeadUpAngle: bindPublicMethod("setHeadUpAngle"),
96
111
  setImuEnabled: bindPublicMethod("setImuEnabled"),
97
112
  setScreenDisabled: bindPublicMethod("setScreenDisabled"),
113
+ ping: bindPublicMethod("ping"),
98
114
  requestWifiScan: bindPublicMethod("requestWifiScan"),
99
115
  sendWifiCredentials: bindPublicMethod("sendWifiCredentials"),
100
116
  forgetWifiNetwork: bindPublicMethod("forgetWifiNetwork"),
101
117
  setHotspotState: bindPublicMethod("setHotspotState"),
118
+ setSystemTime: bindPublicMethod("setSystemTime"),
102
119
  setWifiAdbState: bindPublicMethod("setWifiAdbState"),
103
120
  setGalleryModeEnabled: bindPublicMethod("setGalleryModeEnabled"),
104
121
  setVoiceActivityDetectionEnabled: bindPublicMethod("setVoiceActivityDetectionEnabled"),
@@ -143,7 +160,9 @@ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
143
160
  setOtaVersionUrl: bindPublicMethod("setOtaVersionUrl"),
144
161
  getOtaVersionUrl: bindPublicMethod("getOtaVersionUrl"),
145
162
  checkForOtaUpdate: bindPublicMethod("checkForOtaUpdate"),
163
+ getReleaseChangelogs,
146
164
  startOtaUpdate,
165
+ queryOtaStatus,
147
166
  startAr99OtaFromFile: bindPublicMethod("startAr99OtaFromFile"),
148
167
  cancelAr99Ota: bindPublicMethod("cancelAr99Ota"),
149
168
  sendAr99FactoryReset: bindPublicMethod("sendAr99FactoryReset"),
@@ -212,6 +231,7 @@ export type {
212
231
  HotspotErrorEvent,
213
232
  HotspotStatus,
214
233
  HotspotStatusChangeEvent,
234
+ GlassesSessionChangedEvent,
215
235
  KeepAliveAckEvent,
216
236
  LocalTranscriptionEvent,
217
237
  LogEvent,
@@ -228,6 +248,8 @@ export type {
228
248
  OtaStatusEvent,
229
249
  OtaQueryResult,
230
250
  OtaUpdateInfo,
251
+ ReleaseChangelog,
252
+ MtkUpdateCompleteEvent,
231
253
  PairFailureEvent,
232
254
  PairingInfoEvent,
233
255
  EnteringPairingModeEvent,
@@ -238,6 +260,8 @@ export type {
238
260
  PhotoFpsRange,
239
261
  PhotoMeteredPreview,
240
262
  PhotoMode,
263
+ PublicBluetoothStatus,
264
+ PublicGlassesStatus,
241
265
  PhotoTransferMethod,
242
266
  PhotoResponseEvent,
243
267
  PhotoRequestedCaptureConfig,
@@ -0,0 +1,116 @@
1
+ // This public adapter is the package-owned boundary over its native implementation.
2
+ // eslint-disable-next-line no-restricted-imports
3
+ import MentraLocalNetwork from "../_private/MentraLocalNetworkModule"
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import MentraOtaServer from "../ota-server"
6
+
7
+ export type OtaTransportSubscription = {remove(): void}
8
+
9
+ export type OtaLocalNetworkConnection = {
10
+ connected: boolean
11
+ ssid: string
12
+ localAddress?: string
13
+ }
14
+
15
+ export type OtaLocalNetworkLostEvent = {ssid: string}
16
+
17
+ export type OtaLocalNetworkResponse = {
18
+ status: number
19
+ headers: Record<string, string>
20
+ bodyBase64: string
21
+ }
22
+
23
+ export type OtaLocalNetworkDownloadResult = {
24
+ statusCode: number
25
+ bytesWritten: number
26
+ headers: Record<string, string>
27
+ }
28
+
29
+ export type OtaLocalNetworkDownloadProgress = {
30
+ requestId: string
31
+ bytesWritten: number
32
+ contentLength: number
33
+ statusCode?: number
34
+ headers?: Record<string, string>
35
+ }
36
+
37
+ export type OtaArtifactDownloadProgress = {
38
+ destination: string
39
+ bytesWritten: number
40
+ contentLength: number
41
+ }
42
+
43
+ export type OtaServerResult = {
44
+ baseUrl: string
45
+ host: string
46
+ manifestUrl: string
47
+ port: number
48
+ }
49
+
50
+ /**
51
+ * Scoped networking for traffic that must stay on a Mentra Live hotspot while
52
+ * the phone's default route remains on cellular or another Wi-Fi network.
53
+ *
54
+ * The module is optional because iOS uses its system Wi-Fi join flow instead.
55
+ */
56
+ export const otaLocalNetwork = Object.freeze({
57
+ isAvailable: (): boolean => MentraLocalNetwork != null,
58
+ connect: async (ssid: string, password: string): Promise<OtaLocalNetworkConnection> => {
59
+ if (!MentraLocalNetwork) throw new Error("Scoped local networking is not available in this native build")
60
+ return MentraLocalNetwork.connect(ssid, password)
61
+ },
62
+ request: async (
63
+ requestId: string,
64
+ url: string,
65
+ method: string,
66
+ headers: Record<string, string>,
67
+ body: string | null,
68
+ timeoutMs: number,
69
+ ): Promise<OtaLocalNetworkResponse> => {
70
+ if (!MentraLocalNetwork) throw new Error("Scoped local networking is not available in this native build")
71
+ return MentraLocalNetwork.request(requestId, url, method, headers, body, timeoutMs)
72
+ },
73
+ download: async (
74
+ requestId: string,
75
+ url: string,
76
+ destination: string,
77
+ headers: Record<string, string>,
78
+ connectionTimeoutMs: number,
79
+ readTimeoutMs: number,
80
+ ): Promise<OtaLocalNetworkDownloadResult> => {
81
+ if (!MentraLocalNetwork) throw new Error("Scoped local networking is not available in this native build")
82
+ return MentraLocalNetwork.download(requestId, url, destination, headers, connectionTimeoutMs, readTimeoutMs)
83
+ },
84
+ cancel: async (requestId: string): Promise<void> => {
85
+ if (!MentraLocalNetwork) return
86
+ await MentraLocalNetwork.cancel(requestId)
87
+ },
88
+ disconnect: async (): Promise<void> => {
89
+ if (!MentraLocalNetwork) return
90
+ await MentraLocalNetwork.disconnect()
91
+ },
92
+ onDownloadProgress: (listener: (event: OtaLocalNetworkDownloadProgress) => void): OtaTransportSubscription => {
93
+ if (!MentraLocalNetwork) return {remove() {}}
94
+ return MentraLocalNetwork.addListener("downloadProgress", listener)
95
+ },
96
+ onNetworkLost: (listener: (event: OtaLocalNetworkLostEvent) => void): OtaTransportSubscription => {
97
+ if (!MentraLocalNetwork) return {remove() {}}
98
+ return MentraLocalNetwork.addListener("networkLost", listener)
99
+ },
100
+ })
101
+
102
+ /** Native HTTP server and artifact downloader used by the Engine OTA coordinator. */
103
+ export const otaServer = Object.freeze({
104
+ start: (
105
+ manifestJson: string,
106
+ artifactPaths: Record<string, string>,
107
+ host?: string | null,
108
+ ): Promise<OtaServerResult> => MentraOtaServer.startOtaServer(manifestJson, artifactPaths, host),
109
+ stop: (): Promise<void> => MentraOtaServer.stopOtaServer(),
110
+ waitForWifiAddress: (gateway: string, timeoutMs: number): Promise<string> =>
111
+ MentraOtaServer.waitForWifiAddress(gateway, timeoutMs),
112
+ downloadArtifact: (source: string, destination: string): Promise<{statusCode: number; bytesWritten: number}> =>
113
+ MentraOtaServer.downloadArtifact(source, destination),
114
+ onArtifactDownloadProgress: (listener: (event: OtaArtifactDownloadProgress) => void): OtaTransportSubscription =>
115
+ MentraOtaServer.addListener("artifactDownloadProgress", listener),
116
+ })