@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.
- package/README.md +40 -34
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +0 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +13 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +7 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +15 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/ReleaseChangelog.kt +61 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +81 -69
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
- package/build/BluetoothSdk.types.d.ts +26 -5
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/changelogs.d.ts +7 -0
- package/build/changelogs.d.ts.map +1 -0
- package/build/changelogs.js +44 -0
- package/build/changelogs.js.map +1 -0
- package/build/generated/changelogCatalog.d.ts +6 -0
- package/build/generated/changelogCatalog.d.ts.map +1 -0
- package/build/generated/changelogCatalog.js +8 -0
- package/build/generated/changelogCatalog.js.map +1 -0
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +14 -0
- package/build/index.js.map +1 -1
- package/build/ota-transport/index.d.ts +67 -0
- package/build/ota-transport/index.d.ts.map +1 -0
- package/build/ota-transport/index.js +58 -0
- package/build/ota-transport/index.js.map +1 -0
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/Bridge.swift +1 -6
- package/ios/Source/DeviceManager.swift +13 -8
- package/ios/Source/GeneratedChangelogCatalog.swift +6 -0
- package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
- package/ios/Source/MentraBluetoothSDK.swift +17 -0
- package/ios/Source/ReleaseChangelog.swift +65 -0
- package/ios/Source/sgcs/MentraLive.swift +93 -80
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
- package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
- package/ios/Tests/ReleaseChangelogTests.swift +13 -0
- package/package.json +6 -1
- package/scripts/public-ota-api.test.mjs +37 -0
- package/src/BluetoothSdk.types.ts +28 -5
- package/src/changelogs.ts +44 -0
- package/src/generated/changelogCatalog.ts +9 -0
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/index.ts +24 -0
- 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.
|
|
16
|
-
"releaseSetId": "mentra-3.1.0-dev.
|
|
17
|
-
"sourceCommit": "
|
|
18
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-v3.1.0
|
|
19
|
-
"otaManifestSha256": "
|
|
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
|
+
})
|