@mentra/bluetooth-sdk 0.1.21-beta.3 → 3.1.0-dev.4
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 +38 -2
- 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 +39 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +209 -21
- 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 +1381 -284
- 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/utils/BleJsonCompactTest.java +148 -7
- package/build/BluetoothSdk.types.d.ts +63 -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 +47 -3
- package/ios/Source/DeviceManager.swift +146 -16
- 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 +498 -77
- 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/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 +74 -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,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,28 @@ export type PairFailureEvent = {
|
|
|
648
650
|
error: string
|
|
649
651
|
}
|
|
650
652
|
|
|
653
|
+
export type PairingInfoEvent = {
|
|
654
|
+
had_previous_bond: boolean
|
|
655
|
+
/** 16-char uppercase hex transfer id when secure pairing is active. */
|
|
656
|
+
transfer_id?: string
|
|
657
|
+
pairing_code?: string
|
|
658
|
+
classic_bond_ready?: boolean
|
|
659
|
+
secure_pairing_capable?: boolean
|
|
660
|
+
protocol_version?: number
|
|
661
|
+
/** Credential binding mode negotiated for this transfer, when reported by the glasses. */
|
|
662
|
+
binding?: "ctkd" | "temporal" | "none" | string
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export type EnteringPairingModeEvent = {
|
|
666
|
+
window_ms: number
|
|
667
|
+
reason?: string
|
|
668
|
+
txn?: number
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export type OwnerReplacedEvent = {
|
|
672
|
+
reason: string
|
|
673
|
+
}
|
|
674
|
+
|
|
651
675
|
export type AudioPairingNeededEvent = {
|
|
652
676
|
type: "audio_pairing_needed"
|
|
653
677
|
deviceName: string
|
|
@@ -701,6 +725,17 @@ export type MicLc3Event = {
|
|
|
701
725
|
voiceActivityDetectionEnabled: boolean
|
|
702
726
|
}
|
|
703
727
|
|
|
728
|
+
/** Native glasses-microphone diagnostics emitted when the SDK detects a transport or decode issue. */
|
|
729
|
+
export type MicHealthEvent = {
|
|
730
|
+
type: "mic_health"
|
|
731
|
+
reason: "sequence_gap" | "decode_failure"
|
|
732
|
+
sequenceGapEvents: number
|
|
733
|
+
decodeFailures: number
|
|
734
|
+
lastLc3ReceivedAt?: number
|
|
735
|
+
lastPcmProducedAt?: number
|
|
736
|
+
timestamp: number
|
|
737
|
+
}
|
|
738
|
+
|
|
704
739
|
export type StreamStatusLifecycleState = "initializing" | "streaming" | "stopping" | "stopped"
|
|
705
740
|
export type StreamStatusReconnectState = "reconnecting" | "reconnected" | "reconnect_failed"
|
|
706
741
|
export type StreamStatusState = StreamStatusLifecycleState | StreamStatusReconnectState | "error"
|
|
@@ -895,6 +930,9 @@ export type BluetoothSdkModuleEvents = {
|
|
|
895
930
|
rgb_led_control_response: (event: RgbLedControlResponseEvent) => void
|
|
896
931
|
settings_ack: (event: SettingsAckEvent) => void
|
|
897
932
|
pair_failure: (event: PairFailureEvent) => void
|
|
933
|
+
pairing_info: (event: PairingInfoEvent) => void
|
|
934
|
+
entering_pairing_mode: (event: EnteringPairingModeEvent) => void
|
|
935
|
+
owner_replaced: (event: OwnerReplacedEvent) => void
|
|
898
936
|
audio_pairing_needed: (event: AudioPairingNeededEvent) => void
|
|
899
937
|
audio_connected: (event: AudioConnectedEvent) => void
|
|
900
938
|
audio_disconnected: (event: AudioDisconnectedEvent) => void
|
|
@@ -903,6 +941,7 @@ export type BluetoothSdkModuleEvents = {
|
|
|
903
941
|
ws_bin: (event: WsBinEvent) => void
|
|
904
942
|
mic_pcm: (event: MicPcmEvent) => void
|
|
905
943
|
mic_lc3: (event: MicLc3Event) => void
|
|
944
|
+
mic_health: (event: MicHealthEvent) => void
|
|
906
945
|
stream_status: (event: StreamStatusEvent) => void
|
|
907
946
|
keep_alive_ack: (event: KeepAliveAckEvent) => void
|
|
908
947
|
mtk_update_complete: (event: MtkUpdateCompleteEvent) => void
|
|
@@ -999,11 +1038,15 @@ export type BluetoothSdkEventMap = {
|
|
|
999
1038
|
rgb_led_control_response: RgbLedControlResponseEvent
|
|
1000
1039
|
settings_ack: SettingsAckEvent
|
|
1001
1040
|
pair_failure: PairFailureEvent
|
|
1041
|
+
pairing_info: PairingInfoEvent
|
|
1042
|
+
entering_pairing_mode: EnteringPairingModeEvent
|
|
1043
|
+
owner_replaced: OwnerReplacedEvent
|
|
1002
1044
|
audio_pairing_needed: AudioPairingNeededEvent
|
|
1003
1045
|
audio_connected: AudioConnectedEvent
|
|
1004
1046
|
audio_disconnected: AudioDisconnectedEvent
|
|
1005
1047
|
mic_pcm: MicPcmEvent
|
|
1006
1048
|
mic_lc3: MicLc3Event
|
|
1049
|
+
mic_health: MicHealthEvent
|
|
1007
1050
|
stream_status: StreamStatusEvent
|
|
1008
1051
|
ota_start_ack: OtaStartAckEvent
|
|
1009
1052
|
ota_status: OtaStatusEvent
|
|
@@ -1056,6 +1099,8 @@ export interface BluetoothSdkPublicModule {
|
|
|
1056
1099
|
sendWifiCredentials(ssid: string, password: string): Promise<WifiStatusChangeEvent>
|
|
1057
1100
|
forgetWifiNetwork(ssid: string): Promise<WifiStatusChangeEvent>
|
|
1058
1101
|
setHotspotState(enabled: boolean): Promise<HotspotStatusChangeEvent>
|
|
1102
|
+
/** Enable or disable Wi-Fi ADB on Mentra Live (no-op on other devices). */
|
|
1103
|
+
setWifiAdbState(enabled: boolean): Promise<void>
|
|
1059
1104
|
|
|
1060
1105
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
1061
1106
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>
|
|
@@ -1110,6 +1155,8 @@ export interface BluetoothSdkPublicModule {
|
|
|
1110
1155
|
webhookUrl?: string,
|
|
1111
1156
|
authToken?: string,
|
|
1112
1157
|
): Promise<VideoRecordingStoppedStatusEvent>
|
|
1158
|
+
/** Query the glasses for the current recording state and elapsed duration. */
|
|
1159
|
+
queryVideoRecordingStatus(requestId: string): Promise<VideoRecordingStatusEvent>
|
|
1113
1160
|
|
|
1114
1161
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
1115
1162
|
stopStream(): Promise<StreamStatusEvent>
|
|
@@ -1131,14 +1178,27 @@ export interface BluetoothSdkPublicModule {
|
|
|
1131
1178
|
): Promise<RgbLedControlSuccessResponseEvent>
|
|
1132
1179
|
|
|
1133
1180
|
requestVersionInfo(): Promise<VersionInfoResult>
|
|
1181
|
+
/**
|
|
1182
|
+
* Select the OTA manifest used by subsequent update checks and installs.
|
|
1183
|
+
* The URL may point at Mentra's hosted manifest or any customer-controlled HTTP(S) server.
|
|
1184
|
+
*/
|
|
1185
|
+
setOtaVersionUrl(otaVersionUrl: string): void
|
|
1186
|
+
/** Return the configured or release-embedded OTA manifest URL. Rejects when a source build is unconfigured. */
|
|
1187
|
+
getOtaVersionUrl(): string
|
|
1134
1188
|
/** Fetch the configured OTA manifest and return whether any ASG/BES/MTK update is available. */
|
|
1135
1189
|
checkForOtaUpdate(): Promise<boolean>
|
|
1136
|
-
/** Start
|
|
1137
|
-
startOtaUpdate(): Promise<OtaStartAckEvent>
|
|
1190
|
+
/** Start OTA from the configured or explicitly supplied manifest URL. */
|
|
1191
|
+
startOtaUpdate(otaVersionUrl?: string | null): Promise<OtaStartAckEvent>
|
|
1138
1192
|
startAr99OtaFromFile(path: string): Promise<boolean>
|
|
1139
1193
|
cancelAr99Ota(): Promise<void>
|
|
1140
1194
|
sendAr99FactoryReset(): Promise<void>
|
|
1141
|
-
buildAr99OtaSignature(
|
|
1195
|
+
buildAr99OtaSignature(
|
|
1196
|
+
secret: string,
|
|
1197
|
+
appName: string,
|
|
1198
|
+
currentVersion: string,
|
|
1199
|
+
serialNumber: string,
|
|
1200
|
+
nonce: string,
|
|
1201
|
+
): string
|
|
1142
1202
|
|
|
1143
1203
|
// // stt commands (MOVE TO CRUST)
|
|
1144
1204
|
// setSttModelDetails(path: string, languageCode: string): Promise<void>
|
|
@@ -1201,6 +1261,8 @@ export interface OtaUpdateInfo {
|
|
|
1201
1261
|
updates: string[] // ["apk", "mtk", "bes"]
|
|
1202
1262
|
totalSize: number
|
|
1203
1263
|
cacheReady?: boolean
|
|
1264
|
+
/** Exact BES target selected from the OTA manifest, when a BES step is pending. */
|
|
1265
|
+
besVersion?: string
|
|
1204
1266
|
/** True when the APK step installs an older build than the glasses currently run (exact-pin manifests only). */
|
|
1205
1267
|
isDowngrade?: boolean
|
|
1206
1268
|
}
|
|
@@ -1231,6 +1293,7 @@ export interface GlassesStatus {
|
|
|
1231
1293
|
besFirmwareVersion: string
|
|
1232
1294
|
mtkFirmwareVersion: string
|
|
1233
1295
|
bluetoothMacAddress: string
|
|
1296
|
+
wifiMacAddress: string
|
|
1234
1297
|
leftMacAddress: string
|
|
1235
1298
|
rightMacAddress: string
|
|
1236
1299
|
buildNumber: string
|
|
@@ -1238,6 +1301,8 @@ export interface GlassesStatus {
|
|
|
1238
1301
|
systemTimeMs?: number
|
|
1239
1302
|
otaVersionUrl: string
|
|
1240
1303
|
appVersion: string
|
|
1304
|
+
/** Phone-served hotspot OTA protocol version; 0 means unsupported/legacy glasses. */
|
|
1305
|
+
hotspotOtaVersion: number
|
|
1241
1306
|
bluetoothName: string
|
|
1242
1307
|
serialNumber: string
|
|
1243
1308
|
style: string
|
|
@@ -1301,6 +1366,12 @@ export interface Device {
|
|
|
1301
1366
|
* appear in a later scan update when the platform reports RSSI metadata.
|
|
1302
1367
|
*/
|
|
1303
1368
|
rssi?: number
|
|
1369
|
+
/** Mentra Live: unit is currently in pairing mode (adv flag). */
|
|
1370
|
+
pairingMode?: boolean
|
|
1371
|
+
/** Mentra Live: four-character hex spoken pairing code when available. */
|
|
1372
|
+
pairingCode?: string
|
|
1373
|
+
/** Mentra Live: advertisement carries the secure-pairing capability trailer. */
|
|
1374
|
+
securePairingCapable?: boolean
|
|
1304
1375
|
}
|
|
1305
1376
|
|
|
1306
1377
|
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.4",
|
|
16
|
+
"releaseSetId": "mentra-3.1.0-dev.4",
|
|
17
|
+
"sourceCommit": "43b81aea1c84675fade024077063e45fddde4861",
|
|
18
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-v3.1.0-dev.4/mentra-live-ota-3.1.0-dev.4.json",
|
|
19
|
+
"otaManifestSha256": "8fbea7589345658dc5dffcb394229ffc5c52fbe75352acb8f6827d83290ce750"
|
|
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")
|