@mentra/bluetooth-sdk 0.1.21-beta.3 → 3.1.0-dev.10
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 +56 -9
- package/android/build.gradle +7 -4
- package/android/lc3Lib/build.gradle +18 -4
- package/android/silero/build.gradle +9 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +16 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +35 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +216 -23
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +2 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +112 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +93 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +5 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +15 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +24 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/net/LocalIpv4.kt +57 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/otaserver/MentraOtaServerModule.kt +120 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +2 -49
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +61 -30
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +1344 -284
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +17 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +12 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BleJsonCompact.java +74 -79
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +3 -3
- package/android/src/test/java/com/mentra/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +75 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/BleJsonCompactTest.java +148 -7
- package/build/BluetoothSdk.types.d.ts +58 -2
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +2 -0
- package/build/_internal.d.ts.map +1 -1
- package/build/_internal.js +2 -0
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +4 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +1 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/MentraLocalNetworkModule.d.ts +1 -0
- package/build/_private/MentraLocalNetworkModule.d.ts.map +1 -1
- package/build/_private/MentraLocalNetworkModule.js.map +1 -1
- package/build/_private/sdkOtaManifest.d.ts +3 -12
- package/build/_private/sdkOtaManifest.d.ts.map +1 -1
- package/build/_private/sdkOtaManifest.js +5 -17
- package/build/_private/sdkOtaManifest.js.map +1 -1
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -2
- package/build/index.js.map +1 -1
- package/build/ota-server/MentraOtaServer.types.d.ts +14 -0
- package/build/ota-server/MentraOtaServer.types.d.ts.map +1 -0
- package/build/ota-server/MentraOtaServer.types.js +2 -0
- package/build/ota-server/MentraOtaServer.types.js.map +1 -0
- package/build/ota-server/MentraOtaServerModule.d.ts +21 -0
- package/build/ota-server/MentraOtaServerModule.d.ts.map +1 -0
- package/build/ota-server/MentraOtaServerModule.js +3 -0
- package/build/ota-server/MentraOtaServerModule.js.map +1 -0
- package/build/ota-server/MentraOtaServerModule.web.d.ts +10 -0
- package/build/ota-server/MentraOtaServerModule.web.d.ts.map +1 -0
- package/build/ota-server/MentraOtaServerModule.web.js +12 -0
- package/build/ota-server/MentraOtaServerModule.web.js.map +1 -0
- package/build/ota-server/index.d.ts +3 -0
- package/build/ota-server/index.d.ts.map +1 -0
- package/build/ota-server/index.js +3 -0
- package/build/ota-server/index.js.map +1 -0
- package/expo-module.config.json +3 -2
- package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
- package/ios/BluetoothSdkModule.swift +16 -2
- package/ios/LocalIPv4.swift +150 -0
- package/ios/LocalOtaServer.swift +309 -0
- package/ios/MentraBluetoothSDK.podspec +5 -1
- package/ios/MentraOtaServerModule.swift +147 -0
- package/ios/MentraPhotoReceiverModule.swift +1 -81
- package/ios/Source/BluetoothSdkDefaults.swift +4 -1
- package/ios/Source/Bridge.swift +43 -4
- package/ios/Source/DeviceManager.swift +154 -19
- package/ios/Source/DeviceStore.swift +2 -0
- package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
- package/ios/Source/MentraBluetoothSDK.swift +74 -6
- package/ios/Source/OtaManifest.swift +5 -7
- package/ios/Source/audio/AudioModels.swift +17 -0
- package/ios/Source/camera/CameraModels.swift +1 -0
- package/ios/Source/events/BluetoothEvents.swift +27 -0
- package/ios/Source/sgcs/MentraLive.swift +469 -77
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
- package/ios/Source/sgcs/SGCManager.swift +11 -0
- package/ios/Source/status/DeviceStatus.swift +23 -0
- package/ios/Source/types/DeviceModels.swift +23 -2
- package/ios/Source/utils/BleJsonCompact.swift +73 -71
- package/ios/Tests/MentraLivePairingAdvertisementTests.swift +104 -0
- package/package.json +5 -1
- package/plugin/build/withIos.d.ts +3 -1
- package/plugin/build/withIos.js +22 -10
- package/scripts/verify-release-package.mjs +34 -0
- package/scripts/write-release-metadata.mjs +154 -0
- package/scripts/write-release-metadata.test.mjs +29 -0
- package/src/BluetoothSdk.types.ts +69 -3
- package/src/_internal.ts +2 -0
- package/src/_private/BluetoothSdkModule.ts +5 -5
- package/src/_private/MentraLocalNetworkModule.ts +1 -1
- package/src/_private/sdkOtaManifest.ts +5 -17
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +19 -2
- package/src/ota-server/MentraOtaServer.types.ts +10 -0
- package/src/ota-server/MentraOtaServerModule.ts +23 -0
- package/src/ota-server/MentraOtaServerModule.web.ts +17 -0
- package/src/ota-server/index.ts +2 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {readFileSync} from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
|
|
5
|
+
function option(name) {
|
|
6
|
+
const index = process.argv.indexOf(`--${name}`)
|
|
7
|
+
if (index < 0 || !process.argv[index + 1]) throw new Error(`Missing --${name}`)
|
|
8
|
+
return process.argv[index + 1]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const packageRoot = path.resolve(option("package-root"))
|
|
12
|
+
const releaseIdentity = option("release-identity")
|
|
13
|
+
const otaManifestUrl = option("ota-manifest-url")
|
|
14
|
+
const otaManifestSha256 = option("ota-manifest-sha256")
|
|
15
|
+
const packageJson = JSON.parse(readFileSync(path.join(packageRoot, "package.json"), "utf8"))
|
|
16
|
+
if (packageJson.version !== releaseIdentity) {
|
|
17
|
+
throw new Error(`Packed SDK version ${JSON.stringify(packageJson.version)} does not match ${releaseIdentity}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const files = [
|
|
21
|
+
"src/generated/releaseMetadata.ts",
|
|
22
|
+
"build/generated/releaseMetadata.js",
|
|
23
|
+
"android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt",
|
|
24
|
+
"ios/Source/GeneratedReleaseMetadata.swift",
|
|
25
|
+
]
|
|
26
|
+
for (const relativePath of files) {
|
|
27
|
+
const contents = readFileSync(path.join(packageRoot, relativePath), "utf8")
|
|
28
|
+
if (!contents.includes(otaManifestUrl)) throw new Error(`${relativePath} does not contain the OTA manifest URL`)
|
|
29
|
+
if (!contents.includes(otaManifestSha256))
|
|
30
|
+
throw new Error(`${relativePath} does not contain the OTA manifest SHA-256`)
|
|
31
|
+
if (!contents.includes(releaseIdentity)) throw new Error(`${relativePath} does not contain the release identity`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(`Verified packed Bluetooth SDK ${releaseIdentity} metadata across JavaScript, Android, and iOS`)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {mkdirSync, writeFileSync} from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {fileURLToPath} from "node:url"
|
|
5
|
+
|
|
6
|
+
const STABLE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/
|
|
7
|
+
const RELEASE_IDENTITY_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:dev|beta)\.[1-9]\d*)?$/
|
|
8
|
+
const COMMIT_PATTERN = /^[0-9a-f]{40}$/
|
|
9
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/
|
|
10
|
+
|
|
11
|
+
function validateHttpsUrl(value) {
|
|
12
|
+
let url
|
|
13
|
+
try {
|
|
14
|
+
url = new URL(value)
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error(`OTA manifest URL is invalid: ${JSON.stringify(value)}`)
|
|
17
|
+
}
|
|
18
|
+
if (url.protocol !== "https:" || url.username || url.password || url.hash) {
|
|
19
|
+
throw new Error("OTA manifest URL must be credential-free HTTPS without a fragment")
|
|
20
|
+
}
|
|
21
|
+
return url.toString()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function validateReleaseMetadata(input) {
|
|
25
|
+
if (!STABLE_VERSION_PATTERN.test(input.familyBaseVersion)) {
|
|
26
|
+
throw new Error("familyBaseVersion must be a plain X.Y.Z version")
|
|
27
|
+
}
|
|
28
|
+
if (!RELEASE_IDENTITY_PATTERN.test(input.releaseIdentity)) {
|
|
29
|
+
throw new Error("releaseIdentity must be X.Y.Z, X.Y.Z-dev.N, or X.Y.Z-beta.N")
|
|
30
|
+
}
|
|
31
|
+
if (
|
|
32
|
+
input.releaseIdentity !== input.familyBaseVersion &&
|
|
33
|
+
!input.releaseIdentity.startsWith(`${input.familyBaseVersion}-`)
|
|
34
|
+
) {
|
|
35
|
+
throw new Error("releaseIdentity does not belong to familyBaseVersion")
|
|
36
|
+
}
|
|
37
|
+
if (input.releaseSetId !== `mentra-${input.releaseIdentity}`) {
|
|
38
|
+
throw new Error("releaseSetId must be mentra-<releaseIdentity>")
|
|
39
|
+
}
|
|
40
|
+
if (!COMMIT_PATTERN.test(input.sourceCommit)) throw new Error("sourceCommit must be a full lowercase Git SHA")
|
|
41
|
+
if (!SHA256_PATTERN.test(input.otaManifestSha256)) {
|
|
42
|
+
throw new Error("otaManifestSha256 must be a lowercase SHA-256 digest")
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
schemaVersion: 1,
|
|
46
|
+
familyBaseVersion: input.familyBaseVersion,
|
|
47
|
+
releaseIdentity: input.releaseIdentity,
|
|
48
|
+
releaseSetId: input.releaseSetId,
|
|
49
|
+
sourceCommit: input.sourceCommit,
|
|
50
|
+
otaManifestUrl: validateHttpsUrl(input.otaManifestUrl),
|
|
51
|
+
otaManifestSha256: input.otaManifestSha256,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function swiftString(value) {
|
|
56
|
+
return JSON.stringify(value)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function renderReleaseMetadata(input) {
|
|
60
|
+
const metadata = validateReleaseMetadata(input)
|
|
61
|
+
const typescript = [
|
|
62
|
+
"/** Generated by release CI. Do not edit in a release checkout. */",
|
|
63
|
+
"export interface BluetoothSdkReleaseMetadata {",
|
|
64
|
+
" schemaVersion: 1",
|
|
65
|
+
" familyBaseVersion: string | null",
|
|
66
|
+
" releaseIdentity: string | null",
|
|
67
|
+
" releaseSetId: string | null",
|
|
68
|
+
" sourceCommit: string | null",
|
|
69
|
+
" otaManifestUrl: string | null",
|
|
70
|
+
" otaManifestSha256: string | null",
|
|
71
|
+
"}",
|
|
72
|
+
"",
|
|
73
|
+
`export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze(${JSON.stringify(
|
|
74
|
+
metadata,
|
|
75
|
+
null,
|
|
76
|
+
2,
|
|
77
|
+
)})`,
|
|
78
|
+
"",
|
|
79
|
+
].join("\n")
|
|
80
|
+
const kotlin = [
|
|
81
|
+
"package com.mentra.bluetoothsdk",
|
|
82
|
+
"",
|
|
83
|
+
"/** Generated by release CI. Do not edit in a release checkout. */",
|
|
84
|
+
"internal object GeneratedReleaseMetadata {",
|
|
85
|
+
` const val FAMILY_BASE_VERSION: String = ${JSON.stringify(metadata.familyBaseVersion)}`,
|
|
86
|
+
` const val RELEASE_IDENTITY: String = ${JSON.stringify(metadata.releaseIdentity)}`,
|
|
87
|
+
` const val RELEASE_SET_ID: String = ${JSON.stringify(metadata.releaseSetId)}`,
|
|
88
|
+
` const val SOURCE_COMMIT: String = ${JSON.stringify(metadata.sourceCommit)}`,
|
|
89
|
+
` const val OTA_MANIFEST_URL: String = ${JSON.stringify(metadata.otaManifestUrl)}`,
|
|
90
|
+
` const val OTA_MANIFEST_SHA256: String = ${JSON.stringify(metadata.otaManifestSha256)}`,
|
|
91
|
+
"}",
|
|
92
|
+
"",
|
|
93
|
+
].join("\n")
|
|
94
|
+
const swift = [
|
|
95
|
+
"import Foundation",
|
|
96
|
+
"",
|
|
97
|
+
"/// Generated by release CI. Do not edit in a release checkout.",
|
|
98
|
+
"enum GeneratedReleaseMetadata {",
|
|
99
|
+
` static let familyBaseVersion = ${swiftString(metadata.familyBaseVersion)}`,
|
|
100
|
+
` static let releaseIdentity = ${swiftString(metadata.releaseIdentity)}`,
|
|
101
|
+
` static let releaseSetId = ${swiftString(metadata.releaseSetId)}`,
|
|
102
|
+
` static let sourceCommit = ${swiftString(metadata.sourceCommit)}`,
|
|
103
|
+
` static let otaManifestUrl = ${swiftString(metadata.otaManifestUrl)}`,
|
|
104
|
+
` static let otaManifestSha256 = ${swiftString(metadata.otaManifestSha256)}`,
|
|
105
|
+
"}",
|
|
106
|
+
"",
|
|
107
|
+
].join("\n")
|
|
108
|
+
return {metadata, typescript, kotlin, swift}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseArgs(args) {
|
|
112
|
+
const values = {}
|
|
113
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
114
|
+
const option = args[index]
|
|
115
|
+
const value = args[index + 1]
|
|
116
|
+
if (!option?.startsWith("--") || value === undefined) throw new Error("Expected --name value pairs")
|
|
117
|
+
values[option.slice(2)] = value
|
|
118
|
+
}
|
|
119
|
+
return values
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function main() {
|
|
123
|
+
const args = parseArgs(process.argv.slice(2))
|
|
124
|
+
const required = [
|
|
125
|
+
"family-base-version",
|
|
126
|
+
"release-identity",
|
|
127
|
+
"release-set-id",
|
|
128
|
+
"source-commit",
|
|
129
|
+
"ota-manifest-url",
|
|
130
|
+
"ota-manifest-sha256",
|
|
131
|
+
]
|
|
132
|
+
for (const field of required) if (!args[field]) throw new Error(`Missing --${field}`)
|
|
133
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
134
|
+
const rendered = renderReleaseMetadata({
|
|
135
|
+
familyBaseVersion: args["family-base-version"],
|
|
136
|
+
releaseIdentity: args["release-identity"],
|
|
137
|
+
releaseSetId: args["release-set-id"],
|
|
138
|
+
sourceCommit: args["source-commit"],
|
|
139
|
+
otaManifestUrl: args["ota-manifest-url"],
|
|
140
|
+
otaManifestSha256: args["ota-manifest-sha256"],
|
|
141
|
+
})
|
|
142
|
+
const outputs = {
|
|
143
|
+
typescript: path.join(packageRoot, "src/generated/releaseMetadata.ts"),
|
|
144
|
+
kotlin: path.join(packageRoot, "android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt"),
|
|
145
|
+
swift: path.join(packageRoot, "ios/Source/GeneratedReleaseMetadata.swift"),
|
|
146
|
+
}
|
|
147
|
+
for (const [target, file] of Object.entries(outputs)) {
|
|
148
|
+
mkdirSync(path.dirname(file), {recursive: true})
|
|
149
|
+
writeFileSync(file, rendered[target])
|
|
150
|
+
}
|
|
151
|
+
console.log(`Wrote Bluetooth SDK release metadata for ${rendered.metadata.releaseSetId}`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from "node:assert/strict"
|
|
2
|
+
import test from "node:test"
|
|
3
|
+
|
|
4
|
+
import {renderReleaseMetadata, validateReleaseMetadata} from "./write-release-metadata.mjs"
|
|
5
|
+
|
|
6
|
+
const input = {
|
|
7
|
+
familyBaseVersion: "3.1.0",
|
|
8
|
+
releaseIdentity: "3.1.0-beta.57",
|
|
9
|
+
releaseSetId: "mentra-3.1.0-beta.57",
|
|
10
|
+
sourceCommit: "a".repeat(40),
|
|
11
|
+
otaManifestUrl:
|
|
12
|
+
"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-3.1.0-beta.57/mentra-live-ota-3.1.0-beta.57.json",
|
|
13
|
+
otaManifestSha256: "b".repeat(64),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test("renders identical immutable metadata for TypeScript, Kotlin, and Swift", () => {
|
|
17
|
+
const rendered = renderReleaseMetadata(input)
|
|
18
|
+
for (const contents of [rendered.typescript, rendered.kotlin, rendered.swift]) {
|
|
19
|
+
assert.match(contents, /3\.1\.0-beta\.57/)
|
|
20
|
+
assert.match(contents, /mentra-live-ota-3\.1\.0-beta\.57\.json/)
|
|
21
|
+
assert.match(contents, new RegExp("b".repeat(64)))
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("rejects mutable or cross-family release metadata", () => {
|
|
26
|
+
assert.throws(() => validateReleaseMetadata({...input, releaseIdentity: "3.2.0-beta.57"}), /does not belong/)
|
|
27
|
+
assert.throws(() => validateReleaseMetadata({...input, otaManifestUrl: "http://example.com/version.json"}), /HTTPS/)
|
|
28
|
+
assert.throws(() => validateReleaseMetadata({...input, otaManifestSha256: "short"}), /SHA-256/)
|
|
29
|
+
})
|
|
@@ -155,6 +155,8 @@ export type VersionInfoResult = {
|
|
|
155
155
|
systemTimeMs?: number
|
|
156
156
|
otaVersionUrl: string
|
|
157
157
|
appVersion: string
|
|
158
|
+
/** Phone-served hotspot OTA protocol version; 0 means unsupported/legacy glasses. */
|
|
159
|
+
hotspotOtaVersion: number
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
export type VersionInfoEvent = VersionInfoResult & {
|
|
@@ -648,6 +650,23 @@ export type PairFailureEvent = {
|
|
|
648
650
|
error: string
|
|
649
651
|
}
|
|
650
652
|
|
|
653
|
+
export type PairingInfoEvent = {
|
|
654
|
+
had_previous_bond: boolean
|
|
655
|
+
pairing_code?: string
|
|
656
|
+
classic_bond_ready?: boolean
|
|
657
|
+
secure_pairing_capable?: boolean
|
|
658
|
+
protocol_version?: number
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export type EnteringPairingModeEvent = {
|
|
662
|
+
window_ms: number
|
|
663
|
+
reason?: string
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
export type OwnerReplacedEvent = {
|
|
667
|
+
reason: string
|
|
668
|
+
}
|
|
669
|
+
|
|
651
670
|
export type AudioPairingNeededEvent = {
|
|
652
671
|
type: "audio_pairing_needed"
|
|
653
672
|
deviceName: string
|
|
@@ -701,6 +720,17 @@ export type MicLc3Event = {
|
|
|
701
720
|
voiceActivityDetectionEnabled: boolean
|
|
702
721
|
}
|
|
703
722
|
|
|
723
|
+
/** Native glasses-microphone diagnostics emitted when the SDK detects a transport or decode issue. */
|
|
724
|
+
export type MicHealthEvent = {
|
|
725
|
+
type: "mic_health"
|
|
726
|
+
reason: "sequence_gap" | "decode_failure"
|
|
727
|
+
sequenceGapEvents: number
|
|
728
|
+
decodeFailures: number
|
|
729
|
+
lastLc3ReceivedAt?: number
|
|
730
|
+
lastPcmProducedAt?: number
|
|
731
|
+
timestamp: number
|
|
732
|
+
}
|
|
733
|
+
|
|
704
734
|
export type StreamStatusLifecycleState = "initializing" | "streaming" | "stopping" | "stopped"
|
|
705
735
|
export type StreamStatusReconnectState = "reconnecting" | "reconnected" | "reconnect_failed"
|
|
706
736
|
export type StreamStatusState = StreamStatusLifecycleState | StreamStatusReconnectState | "error"
|
|
@@ -895,6 +925,9 @@ export type BluetoothSdkModuleEvents = {
|
|
|
895
925
|
rgb_led_control_response: (event: RgbLedControlResponseEvent) => void
|
|
896
926
|
settings_ack: (event: SettingsAckEvent) => void
|
|
897
927
|
pair_failure: (event: PairFailureEvent) => void
|
|
928
|
+
pairing_info: (event: PairingInfoEvent) => void
|
|
929
|
+
entering_pairing_mode: (event: EnteringPairingModeEvent) => void
|
|
930
|
+
owner_replaced: (event: OwnerReplacedEvent) => void
|
|
898
931
|
audio_pairing_needed: (event: AudioPairingNeededEvent) => void
|
|
899
932
|
audio_connected: (event: AudioConnectedEvent) => void
|
|
900
933
|
audio_disconnected: (event: AudioDisconnectedEvent) => void
|
|
@@ -903,6 +936,7 @@ export type BluetoothSdkModuleEvents = {
|
|
|
903
936
|
ws_bin: (event: WsBinEvent) => void
|
|
904
937
|
mic_pcm: (event: MicPcmEvent) => void
|
|
905
938
|
mic_lc3: (event: MicLc3Event) => void
|
|
939
|
+
mic_health: (event: MicHealthEvent) => void
|
|
906
940
|
stream_status: (event: StreamStatusEvent) => void
|
|
907
941
|
keep_alive_ack: (event: KeepAliveAckEvent) => void
|
|
908
942
|
mtk_update_complete: (event: MtkUpdateCompleteEvent) => void
|
|
@@ -999,11 +1033,15 @@ export type BluetoothSdkEventMap = {
|
|
|
999
1033
|
rgb_led_control_response: RgbLedControlResponseEvent
|
|
1000
1034
|
settings_ack: SettingsAckEvent
|
|
1001
1035
|
pair_failure: PairFailureEvent
|
|
1036
|
+
pairing_info: PairingInfoEvent
|
|
1037
|
+
entering_pairing_mode: EnteringPairingModeEvent
|
|
1038
|
+
owner_replaced: OwnerReplacedEvent
|
|
1002
1039
|
audio_pairing_needed: AudioPairingNeededEvent
|
|
1003
1040
|
audio_connected: AudioConnectedEvent
|
|
1004
1041
|
audio_disconnected: AudioDisconnectedEvent
|
|
1005
1042
|
mic_pcm: MicPcmEvent
|
|
1006
1043
|
mic_lc3: MicLc3Event
|
|
1044
|
+
mic_health: MicHealthEvent
|
|
1007
1045
|
stream_status: StreamStatusEvent
|
|
1008
1046
|
ota_start_ack: OtaStartAckEvent
|
|
1009
1047
|
ota_status: OtaStatusEvent
|
|
@@ -1056,6 +1094,8 @@ export interface BluetoothSdkPublicModule {
|
|
|
1056
1094
|
sendWifiCredentials(ssid: string, password: string): Promise<WifiStatusChangeEvent>
|
|
1057
1095
|
forgetWifiNetwork(ssid: string): Promise<WifiStatusChangeEvent>
|
|
1058
1096
|
setHotspotState(enabled: boolean): Promise<HotspotStatusChangeEvent>
|
|
1097
|
+
/** Enable or disable Wi-Fi ADB on Mentra Live (no-op on other devices). */
|
|
1098
|
+
setWifiAdbState(enabled: boolean): Promise<void>
|
|
1059
1099
|
|
|
1060
1100
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
1061
1101
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>
|
|
@@ -1110,6 +1150,8 @@ export interface BluetoothSdkPublicModule {
|
|
|
1110
1150
|
webhookUrl?: string,
|
|
1111
1151
|
authToken?: string,
|
|
1112
1152
|
): Promise<VideoRecordingStoppedStatusEvent>
|
|
1153
|
+
/** Query the glasses for the current recording state and elapsed duration. */
|
|
1154
|
+
queryVideoRecordingStatus(requestId: string): Promise<VideoRecordingStatusEvent>
|
|
1113
1155
|
|
|
1114
1156
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
1115
1157
|
stopStream(): Promise<StreamStatusEvent>
|
|
@@ -1131,14 +1173,27 @@ export interface BluetoothSdkPublicModule {
|
|
|
1131
1173
|
): Promise<RgbLedControlSuccessResponseEvent>
|
|
1132
1174
|
|
|
1133
1175
|
requestVersionInfo(): Promise<VersionInfoResult>
|
|
1176
|
+
/**
|
|
1177
|
+
* Select the OTA manifest used by subsequent update checks and installs.
|
|
1178
|
+
* The URL may point at Mentra's hosted manifest or any customer-controlled HTTP(S) server.
|
|
1179
|
+
*/
|
|
1180
|
+
setOtaVersionUrl(otaVersionUrl: string): void
|
|
1181
|
+
/** Return the configured or release-embedded OTA manifest URL. Rejects when a source build is unconfigured. */
|
|
1182
|
+
getOtaVersionUrl(): string
|
|
1134
1183
|
/** Fetch the configured OTA manifest and return whether any ASG/BES/MTK update is available. */
|
|
1135
1184
|
checkForOtaUpdate(): Promise<boolean>
|
|
1136
|
-
/** Start
|
|
1137
|
-
startOtaUpdate(): Promise<OtaStartAckEvent>
|
|
1185
|
+
/** Start OTA from the configured or explicitly supplied manifest URL. */
|
|
1186
|
+
startOtaUpdate(otaVersionUrl?: string | null): Promise<OtaStartAckEvent>
|
|
1138
1187
|
startAr99OtaFromFile(path: string): Promise<boolean>
|
|
1139
1188
|
cancelAr99Ota(): Promise<void>
|
|
1140
1189
|
sendAr99FactoryReset(): Promise<void>
|
|
1141
|
-
buildAr99OtaSignature(
|
|
1190
|
+
buildAr99OtaSignature(
|
|
1191
|
+
secret: string,
|
|
1192
|
+
appName: string,
|
|
1193
|
+
currentVersion: string,
|
|
1194
|
+
serialNumber: string,
|
|
1195
|
+
nonce: string,
|
|
1196
|
+
): string
|
|
1142
1197
|
|
|
1143
1198
|
// // stt commands (MOVE TO CRUST)
|
|
1144
1199
|
// setSttModelDetails(path: string, languageCode: string): Promise<void>
|
|
@@ -1201,6 +1256,8 @@ export interface OtaUpdateInfo {
|
|
|
1201
1256
|
updates: string[] // ["apk", "mtk", "bes"]
|
|
1202
1257
|
totalSize: number
|
|
1203
1258
|
cacheReady?: boolean
|
|
1259
|
+
/** Exact BES target selected from the OTA manifest, when a BES step is pending. */
|
|
1260
|
+
besVersion?: string
|
|
1204
1261
|
/** True when the APK step installs an older build than the glasses currently run (exact-pin manifests only). */
|
|
1205
1262
|
isDowngrade?: boolean
|
|
1206
1263
|
}
|
|
@@ -1231,6 +1288,7 @@ export interface GlassesStatus {
|
|
|
1231
1288
|
besFirmwareVersion: string
|
|
1232
1289
|
mtkFirmwareVersion: string
|
|
1233
1290
|
bluetoothMacAddress: string
|
|
1291
|
+
wifiMacAddress: string
|
|
1234
1292
|
leftMacAddress: string
|
|
1235
1293
|
rightMacAddress: string
|
|
1236
1294
|
buildNumber: string
|
|
@@ -1238,6 +1296,8 @@ export interface GlassesStatus {
|
|
|
1238
1296
|
systemTimeMs?: number
|
|
1239
1297
|
otaVersionUrl: string
|
|
1240
1298
|
appVersion: string
|
|
1299
|
+
/** Phone-served hotspot OTA protocol version; 0 means unsupported/legacy glasses. */
|
|
1300
|
+
hotspotOtaVersion: number
|
|
1241
1301
|
bluetoothName: string
|
|
1242
1302
|
serialNumber: string
|
|
1243
1303
|
style: string
|
|
@@ -1301,6 +1361,12 @@ export interface Device {
|
|
|
1301
1361
|
* appear in a later scan update when the platform reports RSSI metadata.
|
|
1302
1362
|
*/
|
|
1303
1363
|
rssi?: number
|
|
1364
|
+
/** Mentra Live: unit is currently in pairing mode (adv flag). */
|
|
1365
|
+
pairingMode?: boolean
|
|
1366
|
+
/** Mentra Live: four-character hex spoken pairing code when available. */
|
|
1367
|
+
pairingCode?: string
|
|
1368
|
+
/** Mentra Live: advertisement carries the secure-pairing capability trailer. */
|
|
1369
|
+
securePairingCapable?: boolean
|
|
1304
1370
|
}
|
|
1305
1371
|
|
|
1306
1372
|
export interface ConnectOptions {
|
package/src/_internal.ts
CHANGED
|
@@ -11,4 +11,6 @@ export type {BluetoothSdkInternalModule} from "./_private/BluetoothSdkModule"
|
|
|
11
11
|
export * from "./BluetoothSdk.types"
|
|
12
12
|
export {default as MentraLocalNetwork} from "./_private/MentraLocalNetworkModule"
|
|
13
13
|
export * from "./_private/MentraLocalNetworkModule"
|
|
14
|
+
export {default as MentraOtaServer} from "./ota-server"
|
|
15
|
+
export * from "./ota-server/MentraOtaServer.types"
|
|
14
16
|
export {BLUETOOTH_SDK_VERSION, sdkPinnedOtaManifestUrl} from "./_private/sdkOtaManifest"
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
VideoRecordingStartedStatusEvent,
|
|
45
45
|
VideoRecordingSettings,
|
|
46
46
|
VideoRecordingStoppedStatusEvent,
|
|
47
|
+
VideoRecordingStatusEvent,
|
|
47
48
|
VersionInfoResult,
|
|
48
49
|
WarmUpCameraParams,
|
|
49
50
|
WifiSearchResult,
|
|
@@ -117,6 +118,8 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
117
118
|
sendWifiCredentials(ssid: string, password: string): Promise<WifiStatusChangeEvent>
|
|
118
119
|
forgetWifiNetwork(ssid: string): Promise<WifiStatusChangeEvent>
|
|
119
120
|
setHotspotState(enabled: boolean): Promise<HotspotStatusChangeEvent>
|
|
121
|
+
/** Enable or disable Wi-Fi ADB on Mentra Live (no-op on other devices). */
|
|
122
|
+
setWifiAdbState(enabled: boolean): Promise<void>
|
|
120
123
|
/** Set the glasses system clock when phone detects clock skew (Mentra Live and G2). */
|
|
121
124
|
setSystemTime(timestampMs: number): Promise<void>
|
|
122
125
|
/** Logs current WiFi frequency (MHz) and 5 GHz band to Android logcat. */
|
|
@@ -179,6 +182,7 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
179
182
|
webhookUrl?: string,
|
|
180
183
|
authToken?: string,
|
|
181
184
|
): Promise<VideoRecordingStoppedStatusEvent>
|
|
185
|
+
queryVideoRecordingStatus(requestId: string): Promise<VideoRecordingStatusEvent>
|
|
182
186
|
|
|
183
187
|
// Stream Commands
|
|
184
188
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
@@ -293,7 +297,7 @@ const CAMERA_ROI_POSITION_VALUES: Record<CameraRoiPosition, CameraFovSetting["ro
|
|
|
293
297
|
}
|
|
294
298
|
|
|
295
299
|
// Named presets are a convenience layer over the numeric {fov, roiPosition} API.
|
|
296
|
-
// The default is the full sensor; "standard" preserves the historical 102
|
|
300
|
+
// The default is the full sensor; "standard" preserves the historical 102° crop.
|
|
297
301
|
const CAMERA_FOV_PRESETS: Record<CameraFovPreset, CameraFovSetting> = {
|
|
298
302
|
narrow: {fov: 82, roiPosition: 0},
|
|
299
303
|
standard: {fov: 102, roiPosition: 0},
|
|
@@ -649,7 +653,3 @@ NativeBluetoothSdkModule.warmUpCamera = function (params: WarmUpCameraParams) {
|
|
|
649
653
|
|
|
650
654
|
export default NativeBluetoothSdkModule
|
|
651
655
|
export const BluetoothSdk = NativeBluetoothSdkModule as BluetoothSdkInternalModule
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
@@ -26,7 +26,7 @@ type LocalNetworkEvents = {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
declare class MentraLocalNetworkNativeModule extends NativeModule<LocalNetworkEvents> {
|
|
29
|
-
connect(ssid: string, password: string): Promise<{connected: boolean; ssid: string}>
|
|
29
|
+
connect(ssid: string, password: string): Promise<{connected: boolean; ssid: string; localAddress?: string}>
|
|
30
30
|
request(
|
|
31
31
|
requestId: string,
|
|
32
32
|
url: string,
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Each Bluetooth SDK release publishes an immutable manifest
|
|
5
|
-
* (`bluetooth-sdk-<version>-version.json`) pinning the exact ASG artifact paired with that SDK
|
|
6
|
-
* version. The URL is derived from this package's own version — the version is frozen into the
|
|
7
|
-
* published artifact, so this is equivalent to baking the URL at release time while keeping the
|
|
8
|
-
* manifest itself replaceable as an operational escape hatch (asset moves, CDN migration).
|
|
9
|
-
*
|
|
10
|
-
* Mirrors the native derivation in `OtaManifestDefaults.defaultOtaVersionUrl()` (Kotlin) /
|
|
11
|
-
* `BluetoothSdkDefaults` (Swift).
|
|
2
|
+
* Release CI embeds one immutable OTA manifest URL into every SDK distribution.
|
|
3
|
+
* Source builds remain unpinned and must opt in through the debug configuration API.
|
|
12
4
|
*/
|
|
13
5
|
|
|
14
|
-
|
|
6
|
+
import {BLUETOOTH_SDK_RELEASE_METADATA} from "../generated/releaseMetadata"
|
|
15
7
|
|
|
16
8
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
17
9
|
const packageJson = require("../../package.json") as {version?: string}
|
|
@@ -20,12 +12,8 @@ const packageJson = require("../../package.json") as {version?: string}
|
|
|
20
12
|
export const BLUETOOTH_SDK_VERSION: string = (packageJson.version ?? "").trim()
|
|
21
13
|
|
|
22
14
|
/**
|
|
23
|
-
* The immutable manifest URL
|
|
24
|
-
* the package version is unavailable (malformed workspace state).
|
|
15
|
+
* The immutable manifest URL embedded by release CI, or null for an unpinned source build.
|
|
25
16
|
*/
|
|
26
17
|
export function sdkPinnedOtaManifestUrl(): string | null {
|
|
27
|
-
|
|
28
|
-
return null
|
|
29
|
-
}
|
|
30
|
-
return `${SDK_OTA_RELEASE_BASE_URL}/bluetooth-sdk-${BLUETOOTH_SDK_VERSION}-version.json`
|
|
18
|
+
return BLUETOOTH_SDK_RELEASE_METADATA.otaManifestUrl
|
|
31
19
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Generated by release CI. Do not edit in a release checkout. */
|
|
2
|
+
export interface BluetoothSdkReleaseMetadata {
|
|
3
|
+
schemaVersion: 1
|
|
4
|
+
familyBaseVersion: string | null
|
|
5
|
+
releaseIdentity: string | null
|
|
6
|
+
releaseSetId: string | null
|
|
7
|
+
sourceCommit: string | null
|
|
8
|
+
otaManifestUrl: string | null
|
|
9
|
+
otaManifestSha256: string | null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({
|
|
13
|
+
"schemaVersion": 1,
|
|
14
|
+
"familyBaseVersion": "3.1.0",
|
|
15
|
+
"releaseIdentity": "3.1.0-dev.10",
|
|
16
|
+
"releaseSetId": "mentra-3.1.0-dev.10",
|
|
17
|
+
"sourceCommit": "64cf83b23a71e70261571a5f4401d1a7538f1852",
|
|
18
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-v3.1.0-dev.10/mentra-live-ota-3.1.0-dev.10.json",
|
|
19
|
+
"otaManifestSha256": "214d92c8894033eaab276e668cb553c79990005d3330d23aaf499a514dc08cff"
|
|
20
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -36,11 +36,15 @@ const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
|
|
|
36
36
|
"rgb_led_control_response",
|
|
37
37
|
"settings_ack",
|
|
38
38
|
"pair_failure",
|
|
39
|
+
"pairing_info",
|
|
40
|
+
"entering_pairing_mode",
|
|
41
|
+
"owner_replaced",
|
|
39
42
|
"audio_pairing_needed",
|
|
40
43
|
"audio_connected",
|
|
41
44
|
"audio_disconnected",
|
|
42
45
|
"mic_pcm",
|
|
43
46
|
"mic_lc3",
|
|
47
|
+
"mic_health",
|
|
44
48
|
"stream_status",
|
|
45
49
|
"ota_start_ack",
|
|
46
50
|
"ota_status",
|
|
@@ -56,8 +60,8 @@ const addListener: BluetoothSdkPublicModule["addListener"] = (eventName, listene
|
|
|
56
60
|
return PrivateBluetoothSdkModule.addListener(eventName, listener as BluetoothSdkEventListener<BluetoothSdkEventName>)
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
const startOtaUpdate: BluetoothSdkPublicModule["startOtaUpdate"] = () => {
|
|
60
|
-
return PrivateBluetoothSdkModule.startOtaUpdate()
|
|
63
|
+
const startOtaUpdate: BluetoothSdkPublicModule["startOtaUpdate"] = (otaVersionUrl) => {
|
|
64
|
+
return PrivateBluetoothSdkModule.startOtaUpdate(otaVersionUrl)
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
const bindPublicMethod = <K extends keyof BluetoothSdkPublicModule>(name: K): BluetoothSdkPublicModule[K] => {
|
|
@@ -95,6 +99,7 @@ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
|
|
|
95
99
|
sendWifiCredentials: bindPublicMethod("sendWifiCredentials"),
|
|
96
100
|
forgetWifiNetwork: bindPublicMethod("forgetWifiNetwork"),
|
|
97
101
|
setHotspotState: bindPublicMethod("setHotspotState"),
|
|
102
|
+
setWifiAdbState: bindPublicMethod("setWifiAdbState"),
|
|
98
103
|
setGalleryModeEnabled: bindPublicMethod("setGalleryModeEnabled"),
|
|
99
104
|
setVoiceActivityDetectionEnabled: bindPublicMethod("setVoiceActivityDetectionEnabled"),
|
|
100
105
|
setLoudnessGateEnabled: bindPublicMethod("setLoudnessGateEnabled"),
|
|
@@ -125,6 +130,7 @@ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
|
|
|
125
130
|
stopCameraWarmUp: bindPublicMethod("stopCameraWarmUp"),
|
|
126
131
|
startVideoRecording: bindPublicMethod("startVideoRecording"),
|
|
127
132
|
stopVideoRecording: bindPublicMethod("stopVideoRecording"),
|
|
133
|
+
queryVideoRecordingStatus: bindPublicMethod("queryVideoRecordingStatus"),
|
|
128
134
|
startStream: bindPublicMethod("startStream"),
|
|
129
135
|
stopStream: bindPublicMethod("stopStream"),
|
|
130
136
|
setMicState: bindPublicMethod("setMicState"),
|
|
@@ -134,6 +140,8 @@ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
|
|
|
134
140
|
setGlassesMediaVolume: bindPublicMethod("setGlassesMediaVolume"),
|
|
135
141
|
rgbLedControl: bindPublicMethod("rgbLedControl"),
|
|
136
142
|
requestVersionInfo: bindPublicMethod("requestVersionInfo"),
|
|
143
|
+
setOtaVersionUrl: bindPublicMethod("setOtaVersionUrl"),
|
|
144
|
+
getOtaVersionUrl: bindPublicMethod("getOtaVersionUrl"),
|
|
137
145
|
checkForOtaUpdate: bindPublicMethod("checkForOtaUpdate"),
|
|
138
146
|
startOtaUpdate,
|
|
139
147
|
startAr99OtaFromFile: bindPublicMethod("startAr99OtaFromFile"),
|
|
@@ -204,17 +212,26 @@ export type {
|
|
|
204
212
|
HotspotErrorEvent,
|
|
205
213
|
HotspotStatus,
|
|
206
214
|
HotspotStatusChangeEvent,
|
|
215
|
+
KeepAliveAckEvent,
|
|
207
216
|
LocalTranscriptionEvent,
|
|
208
217
|
LogEvent,
|
|
209
218
|
MicLc3Event,
|
|
219
|
+
MicHealthEvent,
|
|
210
220
|
MicMode,
|
|
211
221
|
MicPcmEvent,
|
|
212
222
|
MicPreference,
|
|
213
223
|
OtaStartAckEvent,
|
|
224
|
+
OtaProgress,
|
|
225
|
+
OtaProgressStatus,
|
|
226
|
+
OtaStage,
|
|
214
227
|
OtaStatus,
|
|
215
228
|
OtaStatusEvent,
|
|
216
229
|
OtaQueryResult,
|
|
230
|
+
OtaUpdateInfo,
|
|
217
231
|
PairFailureEvent,
|
|
232
|
+
PairingInfoEvent,
|
|
233
|
+
EnteringPairingModeEvent,
|
|
234
|
+
OwnerReplacedEvent,
|
|
218
235
|
PhotoCaptureMetadata,
|
|
219
236
|
PhotoResolvedConfig,
|
|
220
237
|
PhotoCompression,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type OtaServerResult = {
|
|
2
|
+
baseUrl: string
|
|
3
|
+
host: string
|
|
4
|
+
manifestUrl: string
|
|
5
|
+
port: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type MentraOtaServerModuleEvents = {
|
|
9
|
+
artifactDownloadProgress: (event: {destination: string; bytesWritten: number; contentLength: number}) => void
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {NativeModule, requireNativeModule} from "expo"
|
|
2
|
+
|
|
3
|
+
import type {MentraOtaServerModuleEvents, OtaServerResult} from "./MentraOtaServer.types"
|
|
4
|
+
|
|
5
|
+
declare class MentraOtaServerModule extends NativeModule<MentraOtaServerModuleEvents> {
|
|
6
|
+
/**
|
|
7
|
+
* Serve `manifestJson` at /version.json and `artifactPaths` (sha256 -> local file path)
|
|
8
|
+
* at /artifacts/<sha256>. Pass `host` when the caller knows the authoritative local
|
|
9
|
+
* address (e.g. from the scoped hotspot network); omit to fall back to interface
|
|
10
|
+
* scanning. Returns the URLs to hand to the glasses via ota_start.
|
|
11
|
+
*/
|
|
12
|
+
startOtaServer(
|
|
13
|
+
manifestJson: string,
|
|
14
|
+
artifactPaths: Record<string, string>,
|
|
15
|
+
host?: string | null,
|
|
16
|
+
): Promise<OtaServerResult>
|
|
17
|
+
stopOtaServer(): Promise<void>
|
|
18
|
+
/** Wait for iPhone Wi-Fi (`en0`) to acquire an address on the glasses gateway subnet. */
|
|
19
|
+
waitForWifiAddress(gateway: string, timeoutMs: number): Promise<string>
|
|
20
|
+
downloadArtifact(source: string, destination: string): Promise<{statusCode: number; bytesWritten: number}>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default requireNativeModule<MentraOtaServerModule>("MentraOtaServer")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {NativeModule, registerWebModule} from "expo"
|
|
2
|
+
|
|
3
|
+
import type {MentraOtaServerModuleEvents, OtaServerResult} from "./MentraOtaServer.types"
|
|
4
|
+
|
|
5
|
+
class MentraOtaServerModule extends NativeModule<MentraOtaServerModuleEvents> {
|
|
6
|
+
async startOtaServer(): Promise<OtaServerResult> {
|
|
7
|
+
throw new Error("The OTA server is only available in native apps.")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async stopOtaServer(): Promise<void> {}
|
|
11
|
+
|
|
12
|
+
async waitForWifiAddress(): Promise<string> {
|
|
13
|
+
throw new Error("The OTA server is only available in native apps.")
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default registerWebModule(MentraOtaServerModule, "MentraOtaServer")
|