@mentra/bluetooth-sdk 0.1.21-beta.3 → 3.1.0-beta.100
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 +67 -25
- 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/GeneratedChangelogCatalog.kt +7 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +127 -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/ReleaseChangelog.kt +61 -0
- 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 +1393 -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/ReleaseChangelogTest.kt +13 -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 +133 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/BleJsonCompactTest.java +148 -7
- package/build/BluetoothSdk.types.d.ts +84 -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/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.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 +24 -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/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/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/GeneratedChangelogCatalog.swift +6 -0
- package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
- package/ios/Source/MentraBluetoothSDK.swift +91 -6
- package/ios/Source/OtaManifest.swift +5 -7
- package/ios/Source/ReleaseChangelog.swift +65 -0
- 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 +512 -78
- 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 +160 -0
- package/ios/Tests/ReleaseChangelogTests.swift +13 -0
- package/package.json +10 -1
- package/plugin/build/withIos.d.ts +3 -1
- package/plugin/build/withIos.js +22 -10
- package/scripts/public-ota-api.test.mjs +37 -0
- 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 +97 -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/changelogs.ts +44 -0
- package/src/generated/changelogCatalog.ts +9 -0
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +43 -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
- package/src/ota-transport/index.ts +116 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { type ConfigPlugin } from "expo/config-plugins";
|
|
1
|
+
import { IOSConfig, type ConfigPlugin } from "expo/config-plugins";
|
|
2
2
|
import { type BluetoothSdkPluginProps } from "./index";
|
|
3
|
+
export declare const INFO_SDK_VERSION = "MentraBluetoothSdkVersion";
|
|
4
|
+
export declare function applyBluetoothSdkInfoPlist(infoPlist: IOSConfig.InfoPlist, props: BluetoothSdkPluginProps | undefined): IOSConfig.InfoPlist;
|
|
3
5
|
export declare const withIosConfiguration: ConfigPlugin<BluetoothSdkPluginProps>;
|
package/plugin/build/withIos.js
CHANGED
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.withIosConfiguration = void 0;
|
|
6
|
+
exports.withIosConfiguration = exports.INFO_SDK_VERSION = void 0;
|
|
7
|
+
exports.applyBluetoothSdkInfoPlist = applyBluetoothSdkInfoPlist;
|
|
7
8
|
const child_process_1 = require("child_process");
|
|
8
9
|
const fs_1 = __importDefault(require("fs"));
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
@@ -11,6 +12,7 @@ const config_plugins_1 = require("expo/config-plugins");
|
|
|
11
12
|
const BLUETOOTH_SDK_EXPO_ADAPTER_ENV = "MENTRA_BLUETOOTH_SDK_INCLUDE_EXPO_ADAPTER";
|
|
12
13
|
const BLUETOOTH_SDK_EXPO_ADAPTER_LINE = `ENV['${BLUETOOTH_SDK_EXPO_ADAPTER_ENV}'] ||= '1'`;
|
|
13
14
|
const INFO_ANALYTICS_DISABLED = "MentraBluetoothSdkAnalyticsDisabled";
|
|
15
|
+
exports.INFO_SDK_VERSION = "MentraBluetoothSdkVersion";
|
|
14
16
|
const STALE_INFO_POSTHOG_API_KEY = "MentraBluetoothSdkPostHogApiKey";
|
|
15
17
|
const STALE_INFO_POSTHOG_HOST = "MentraBluetoothSdkPostHogHost";
|
|
16
18
|
const ensureBluetoothSdkExpoAdapterPodEnv = (podfile) => {
|
|
@@ -70,15 +72,25 @@ function resolveAnalyticsProps(props) {
|
|
|
70
72
|
disabled,
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
|
-
function
|
|
75
|
+
function applyBluetoothSdkInfoPlist(infoPlist, props) {
|
|
76
|
+
const packageJson = require("../../package.json");
|
|
77
|
+
const sdkVersion = typeof packageJson.version === "string" ? packageJson.version.trim() : "";
|
|
78
|
+
if (!sdkVersion) {
|
|
79
|
+
throw new Error("@mentra/bluetooth-sdk package.json is missing a version");
|
|
80
|
+
}
|
|
81
|
+
const analytics = resolveAnalyticsProps(props);
|
|
82
|
+
delete infoPlist[INFO_ANALYTICS_DISABLED];
|
|
83
|
+
delete infoPlist[STALE_INFO_POSTHOG_API_KEY];
|
|
84
|
+
delete infoPlist[STALE_INFO_POSTHOG_HOST];
|
|
85
|
+
infoPlist[exports.INFO_SDK_VERSION] = sdkVersion;
|
|
86
|
+
if (analytics.disabled !== undefined) {
|
|
87
|
+
infoPlist[INFO_ANALYTICS_DISABLED] = analytics.disabled;
|
|
88
|
+
}
|
|
89
|
+
return infoPlist;
|
|
90
|
+
}
|
|
91
|
+
function withBluetoothSdkInfoPlist(config, props) {
|
|
74
92
|
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
75
|
-
|
|
76
|
-
delete config.modResults[INFO_ANALYTICS_DISABLED];
|
|
77
|
-
delete config.modResults[STALE_INFO_POSTHOG_API_KEY];
|
|
78
|
-
delete config.modResults[STALE_INFO_POSTHOG_HOST];
|
|
79
|
-
if (analytics.disabled !== undefined) {
|
|
80
|
-
config.modResults[INFO_ANALYTICS_DISABLED] = analytics.disabled;
|
|
81
|
-
}
|
|
93
|
+
config.modResults = applyBluetoothSdkInfoPlist(config.modResults, props);
|
|
82
94
|
return config;
|
|
83
95
|
});
|
|
84
96
|
}
|
|
@@ -87,7 +99,7 @@ const withIosConfiguration = (config, props) => {
|
|
|
87
99
|
config.modResults.contents = ensureBluetoothSdkExpoAdapterPodEnv(config.modResults.contents);
|
|
88
100
|
return config;
|
|
89
101
|
});
|
|
90
|
-
config =
|
|
102
|
+
config = withBluetoothSdkInfoPlist(config, props);
|
|
91
103
|
if (props?.node) {
|
|
92
104
|
config = withXcodeEnvLocal(config);
|
|
93
105
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from "node:assert/strict"
|
|
2
|
+
import {readFileSync} from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import test from "node:test"
|
|
5
|
+
import {fileURLToPath} from "node:url"
|
|
6
|
+
|
|
7
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
8
|
+
|
|
9
|
+
test("publishes the supported OTA transport entrypoint", () => {
|
|
10
|
+
const manifest = JSON.parse(readFileSync(path.join(packageRoot, "package.json"), "utf8"))
|
|
11
|
+
assert.deepEqual(manifest.exports["./ota-transport"], {
|
|
12
|
+
"react-native": "./src/ota-transport/index.ts",
|
|
13
|
+
"types": "./build/ota-transport/index.d.ts",
|
|
14
|
+
"default": "./build/ota-transport/index.js",
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const source = readFileSync(path.join(packageRoot, "src/ota-transport/index.ts"), "utf8")
|
|
18
|
+
assert.match(source, /export const otaLocalNetwork/)
|
|
19
|
+
assert.match(source, /export const otaServer/)
|
|
20
|
+
assert.doesNotMatch(source, /export \{default as Mentra(LocalNetwork|OtaServer)/)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test("keeps restart signals and correlated OTA status on the public root", () => {
|
|
24
|
+
const source = readFileSync(path.join(packageRoot, "src/index.ts"), "utf8")
|
|
25
|
+
for (const required of [
|
|
26
|
+
'"glasses_session_changed"',
|
|
27
|
+
'"mtk_update_complete"',
|
|
28
|
+
"queryOtaStatus",
|
|
29
|
+
"subscribeGlassesStatus",
|
|
30
|
+
"subscribeBluetoothStatus",
|
|
31
|
+
"setSystemTime",
|
|
32
|
+
"ping",
|
|
33
|
+
]) {
|
|
34
|
+
assert.ok(source.includes(required), `missing public OTA primitive: ${required}`)
|
|
35
|
+
}
|
|
36
|
+
assert.match(source, /Unsupported BluetoothSdk event/)
|
|
37
|
+
})
|
|
@@ -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,12 +1033,20 @@ 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
|
|
1046
|
+
/** Mentra Live MTK updater completed and the glasses are about to restart. */
|
|
1047
|
+
mtk_update_complete: MtkUpdateCompleteEvent
|
|
1048
|
+
/** The ASG process restarted while the BES kept the BLE connection alive. */
|
|
1049
|
+
glasses_session_changed: GlassesSessionChangedEvent
|
|
1008
1050
|
ota_start_ack: OtaStartAckEvent
|
|
1009
1051
|
ota_status: OtaStatusEvent
|
|
1010
1052
|
ar99_ota_status: Ar99OtaStatusEvent
|
|
@@ -1030,6 +1072,15 @@ export interface BluetoothSdkPublicModule {
|
|
|
1030
1072
|
listener: BluetoothSdkEventListener<EventName>,
|
|
1031
1073
|
): BluetoothSdkSubscription
|
|
1032
1074
|
|
|
1075
|
+
/** Read an immutable snapshot of the current glasses state. */
|
|
1076
|
+
getGlassesStatus(): Promise<PublicGlassesStatus>
|
|
1077
|
+
/** Read an immutable snapshot of the phone Bluetooth adapter state. */
|
|
1078
|
+
getBluetoothStatus(): Promise<PublicBluetoothStatus>
|
|
1079
|
+
/** Observe immutable glasses-state patches. Returns an unsubscribe function. */
|
|
1080
|
+
subscribeGlassesStatus(listener: (changed: Partial<PublicGlassesStatus>) => void): () => void
|
|
1081
|
+
/** Observe immutable Bluetooth-state patches. Returns an unsubscribe function. */
|
|
1082
|
+
subscribeBluetoothStatus(listener: (changed: Partial<PublicBluetoothStatus>) => void): () => void
|
|
1083
|
+
|
|
1033
1084
|
getDefaultDevice(): Promise<Device | null>
|
|
1034
1085
|
setDefaultDevice(device: Device | null): Promise<void>
|
|
1035
1086
|
clearDefaultDevice(): Promise<void>
|
|
@@ -1051,11 +1102,17 @@ export interface BluetoothSdkPublicModule {
|
|
|
1051
1102
|
setHeadUpAngle(angleDegrees: number): Promise<void>
|
|
1052
1103
|
setImuEnabled(enabled: boolean): Promise<void>
|
|
1053
1104
|
setScreenDisabled(disabled: boolean): Promise<void>
|
|
1105
|
+
/** Keep legacy Mentra Live OTA sessions awake. Modern sessions normally do not require this. */
|
|
1106
|
+
ping(): Promise<void>
|
|
1054
1107
|
|
|
1055
1108
|
requestWifiScan(): Promise<WifiSearchResult[]>
|
|
1056
1109
|
sendWifiCredentials(ssid: string, password: string): Promise<WifiStatusChangeEvent>
|
|
1057
1110
|
forgetWifiNetwork(ssid: string): Promise<WifiStatusChangeEvent>
|
|
1058
1111
|
setHotspotState(enabled: boolean): Promise<HotspotStatusChangeEvent>
|
|
1112
|
+
/** Set the glasses clock from the phone after an OTA clock-skew failure. */
|
|
1113
|
+
setSystemTime(timestampMs: number): Promise<void>
|
|
1114
|
+
/** Enable or disable Wi-Fi ADB on Mentra Live (no-op on other devices). */
|
|
1115
|
+
setWifiAdbState(enabled: boolean): Promise<void>
|
|
1059
1116
|
|
|
1060
1117
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
1061
1118
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>
|
|
@@ -1110,6 +1167,8 @@ export interface BluetoothSdkPublicModule {
|
|
|
1110
1167
|
webhookUrl?: string,
|
|
1111
1168
|
authToken?: string,
|
|
1112
1169
|
): Promise<VideoRecordingStoppedStatusEvent>
|
|
1170
|
+
/** Query the glasses for the current recording state and elapsed duration. */
|
|
1171
|
+
queryVideoRecordingStatus(requestId: string): Promise<VideoRecordingStatusEvent>
|
|
1113
1172
|
|
|
1114
1173
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
1115
1174
|
stopStream(): Promise<StreamStatusEvent>
|
|
@@ -1131,14 +1190,31 @@ export interface BluetoothSdkPublicModule {
|
|
|
1131
1190
|
): Promise<RgbLedControlSuccessResponseEvent>
|
|
1132
1191
|
|
|
1133
1192
|
requestVersionInfo(): Promise<VersionInfoResult>
|
|
1193
|
+
/**
|
|
1194
|
+
* Select the OTA manifest used by subsequent update checks and installs.
|
|
1195
|
+
* The URL may point at Mentra's hosted manifest or any customer-controlled HTTP(S) server.
|
|
1196
|
+
*/
|
|
1197
|
+
setOtaVersionUrl(otaVersionUrl: string): void
|
|
1198
|
+
/** Return the configured or release-embedded OTA manifest URL. Rejects when a source build is unconfigured. */
|
|
1199
|
+
getOtaVersionUrl(): string
|
|
1134
1200
|
/** Fetch the configured OTA manifest and return whether any ASG/BES/MTK update is available. */
|
|
1135
1201
|
checkForOtaUpdate(): Promise<boolean>
|
|
1136
|
-
/**
|
|
1137
|
-
|
|
1202
|
+
/** Return bundled release changelogs crossed between two coordinated product versions, newest first. */
|
|
1203
|
+
getReleaseChangelogs(fromVersion?: string | null, toVersion?: string | null): ReleaseChangelog[]
|
|
1204
|
+
/** Start OTA from the configured or explicitly supplied manifest URL. */
|
|
1205
|
+
startOtaUpdate(otaVersionUrl?: string | null): Promise<OtaStartAckEvent>
|
|
1206
|
+
/** Query the active OTA session and return the correlated status response. */
|
|
1207
|
+
queryOtaStatus(): Promise<OtaQueryResult>
|
|
1138
1208
|
startAr99OtaFromFile(path: string): Promise<boolean>
|
|
1139
1209
|
cancelAr99Ota(): Promise<void>
|
|
1140
1210
|
sendAr99FactoryReset(): Promise<void>
|
|
1141
|
-
buildAr99OtaSignature(
|
|
1211
|
+
buildAr99OtaSignature(
|
|
1212
|
+
secret: string,
|
|
1213
|
+
appName: string,
|
|
1214
|
+
currentVersion: string,
|
|
1215
|
+
serialNumber: string,
|
|
1216
|
+
nonce: string,
|
|
1217
|
+
): string
|
|
1142
1218
|
|
|
1143
1219
|
// // stt commands (MOVE TO CRUST)
|
|
1144
1220
|
// setSttModelDetails(path: string, languageCode: string): Promise<void>
|
|
@@ -1201,10 +1277,19 @@ export interface OtaUpdateInfo {
|
|
|
1201
1277
|
updates: string[] // ["apk", "mtk", "bes"]
|
|
1202
1278
|
totalSize: number
|
|
1203
1279
|
cacheReady?: boolean
|
|
1280
|
+
/** Exact BES target selected from the OTA manifest, when a BES step is pending. */
|
|
1281
|
+
besVersion?: string
|
|
1204
1282
|
/** True when the APK step installs an older build than the glasses currently run (exact-pin manifests only). */
|
|
1205
1283
|
isDowngrade?: boolean
|
|
1206
1284
|
}
|
|
1207
1285
|
|
|
1286
|
+
export interface ReleaseChangelog {
|
|
1287
|
+
/** Base production version, for example `3.1.0`. */
|
|
1288
|
+
version: string
|
|
1289
|
+
/** Markdown body authored in `/changelogs/<version>.md`. */
|
|
1290
|
+
markdown: string
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1208
1293
|
export interface OtaProgress {
|
|
1209
1294
|
stage: OtaStage
|
|
1210
1295
|
status: OtaProgressStatus
|
|
@@ -1231,6 +1316,7 @@ export interface GlassesStatus {
|
|
|
1231
1316
|
besFirmwareVersion: string
|
|
1232
1317
|
mtkFirmwareVersion: string
|
|
1233
1318
|
bluetoothMacAddress: string
|
|
1319
|
+
wifiMacAddress: string
|
|
1234
1320
|
leftMacAddress: string
|
|
1235
1321
|
rightMacAddress: string
|
|
1236
1322
|
buildNumber: string
|
|
@@ -1238,6 +1324,8 @@ export interface GlassesStatus {
|
|
|
1238
1324
|
systemTimeMs?: number
|
|
1239
1325
|
otaVersionUrl: string
|
|
1240
1326
|
appVersion: string
|
|
1327
|
+
/** Phone-served hotspot OTA protocol version; 0 means unsupported/legacy glasses. */
|
|
1328
|
+
hotspotOtaVersion: number
|
|
1241
1329
|
bluetoothName: string
|
|
1242
1330
|
serialNumber: string
|
|
1243
1331
|
style: string
|
|
@@ -1301,6 +1389,12 @@ export interface Device {
|
|
|
1301
1389
|
* appear in a later scan update when the platform reports RSSI metadata.
|
|
1302
1390
|
*/
|
|
1303
1391
|
rssi?: number
|
|
1392
|
+
/** Mentra Live: unit is currently in pairing mode (adv flag). */
|
|
1393
|
+
pairingMode?: boolean
|
|
1394
|
+
/** Mentra Live: four-character hex spoken pairing code when available. */
|
|
1395
|
+
pairingCode?: string
|
|
1396
|
+
/** Mentra Live: advertisement carries the secure-pairing capability trailer. */
|
|
1397
|
+
securePairingCapable?: boolean
|
|
1304
1398
|
}
|
|
1305
1399
|
|
|
1306
1400
|
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
|
}
|