@mentra/bluetooth-sdk 0.1.10 → 0.1.12
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 +52 -3
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +182 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +61 -19
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +24 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +32 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +76 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +17 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +829 -643
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +34 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +40 -64
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +23 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +2 -1
- package/build/BluetoothSdk.types.d.ts +45 -2
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +2 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +14 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -0
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +24 -7
- package/ios/Source/BluetoothSdkDefaults.swift +27 -0
- package/ios/Source/Bridge.swift +16 -0
- package/ios/Source/DeviceManager.swift +33 -27
- package/ios/Source/DeviceStore.swift +8 -2
- package/ios/Source/MentraBluetoothSDK.swift +160 -8
- package/ios/Source/camera/CameraModels.swift +50 -1
- package/ios/Source/events/BluetoothEvents.swift +3 -0
- package/ios/Source/internal/BluetoothAvailability.swift +20 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +160 -0
- package/ios/Source/sgcs/G1.swift +3 -3
- package/ios/Source/sgcs/G2.swift +1022 -640
- package/ios/Source/sgcs/Mach1.swift +2 -2
- package/ios/Source/sgcs/MentraLive.swift +31 -7
- package/ios/Source/sgcs/MentraNex.swift +215 -85
- package/ios/Source/sgcs/SGCManager.swift +26 -6
- package/ios/Source/sgcs/Simulated.swift +2 -2
- package/ios/Source/types/DeviceModels.swift +5 -1
- package/package.json +1 -1
- package/plugin/build/index.d.ts +4 -0
- package/plugin/build/withAndroid.d.ts +2 -3
- package/plugin/build/withAndroid.js +46 -0
- package/plugin/build/withIos.d.ts +2 -3
- package/plugin/build/withIos.js +32 -0
- package/src/BluetoothSdk.types.ts +53 -2
- package/src/_private/BluetoothSdkModule.ts +27 -4
- package/src/index.ts +5 -0
|
@@ -31,9 +31,16 @@ protocol SGCManager {
|
|
|
31
31
|
/// Defaulted in an extension to delegate to the basic recording path; devices
|
|
32
32
|
/// that support custom settings (e.g. Mentra Live) override this.
|
|
33
33
|
func startVideoRecording(
|
|
34
|
-
requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int
|
|
34
|
+
requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int,
|
|
35
|
+
maxRecordingTimeMinutes: Int
|
|
35
36
|
)
|
|
36
37
|
func stopVideoRecording(requestId: String)
|
|
38
|
+
/// Stop recording and upload the result to `webhookUrl` (multipart) using
|
|
39
|
+
/// `authToken`. Supplied at stop time so the token is fresh when the upload
|
|
40
|
+
/// runs. Defaulted in an extension to ignore the upload target and just stop;
|
|
41
|
+
/// devices that support webhook upload (e.g. Mentra Live) override this. An
|
|
42
|
+
/// empty/nil `webhookUrl` means "keep the video on device".
|
|
43
|
+
func stopVideoRecording(requestId: String, webhookUrl: String?, authToken: String?)
|
|
37
44
|
|
|
38
45
|
// MARK: - Button Settings
|
|
39
46
|
|
|
@@ -47,8 +54,8 @@ protocol SGCManager {
|
|
|
47
54
|
|
|
48
55
|
func setBrightness(_ level: Int, autoMode: Bool)
|
|
49
56
|
func clearDisplay()
|
|
50
|
-
func sendTextWall(_ text: String)
|
|
51
|
-
func sendDoubleTextWall(_ top: String, _ bottom: String)
|
|
57
|
+
func sendTextWall(_ text: String) async
|
|
58
|
+
func sendDoubleTextWall(_ top: String, _ bottom: String) async
|
|
52
59
|
/// Display a bitmap. Optional `x`/`y`/`width`/`height` position and size the target
|
|
53
60
|
/// container (used by G2; other SGCs ignore positioning and render the bitmap as before).
|
|
54
61
|
func displayBitmap(base64ImageData: String, x: Int32?, y: Int32?, width: Int32?, height: Int32?) async -> Bool
|
|
@@ -64,7 +71,7 @@ protocol SGCManager {
|
|
|
64
71
|
|
|
65
72
|
// MARK: - Notification Panel
|
|
66
73
|
|
|
67
|
-
func showNotificationsPanel()
|
|
74
|
+
func showNotificationsPanel() async
|
|
68
75
|
|
|
69
76
|
// MARK: - Calendar Events
|
|
70
77
|
|
|
@@ -77,6 +84,9 @@ protocol SGCManager {
|
|
|
77
84
|
// MARK: - Device Control
|
|
78
85
|
|
|
79
86
|
func setHeadUpAngle(_ angle: Int)
|
|
87
|
+
/// Enable/disable raw accelerometer (IMU) reporting from the glasses.
|
|
88
|
+
/// Default no-op; only G2 streams IMU data today.
|
|
89
|
+
func setImuEnabled(_ enabled: Bool) async
|
|
80
90
|
func getBatteryStatus()
|
|
81
91
|
func setSilentMode(_ enabled: Bool)
|
|
82
92
|
func exit()
|
|
@@ -142,11 +152,15 @@ extension SGCManager {
|
|
|
142
152
|
|
|
143
153
|
func startVideoRecording(
|
|
144
154
|
requestId: String, save: Bool, flash: Bool, sound: Bool, width _: Int, height _: Int,
|
|
145
|
-
fps _: Int
|
|
155
|
+
fps _: Int, maxRecordingTimeMinutes _: Int
|
|
146
156
|
) {
|
|
147
157
|
startVideoRecording(requestId: requestId, save: save, flash: flash, sound: sound)
|
|
148
158
|
}
|
|
149
159
|
|
|
160
|
+
func stopVideoRecording(requestId: String, webhookUrl _: String?, authToken _: String?) {
|
|
161
|
+
stopVideoRecording(requestId: requestId)
|
|
162
|
+
}
|
|
163
|
+
|
|
150
164
|
// MARK: - Dashboard (default: combined wire format; Nex implements single-field)
|
|
151
165
|
|
|
152
166
|
func setDashboardHeightOnly(_ height: Int) {
|
|
@@ -165,7 +179,13 @@ extension SGCManager {
|
|
|
165
179
|
|
|
166
180
|
// MARK: - Notification Panel (default no-op — only G2 supports this)
|
|
167
181
|
|
|
168
|
-
func showNotificationsPanel() {}
|
|
182
|
+
func showNotificationsPanel() async {}
|
|
183
|
+
|
|
184
|
+
// MARK: - IMU (default no-op — only G2 streams accelerometer data)
|
|
185
|
+
|
|
186
|
+
func setImuEnabled(_: Bool) async {
|
|
187
|
+
Bridge.log("SGC: setImuEnabled not supported")
|
|
188
|
+
}
|
|
169
189
|
|
|
170
190
|
// MARK: - Calendar Events (default no-op — only G2 supports this)
|
|
171
191
|
|
|
@@ -133,11 +133,11 @@ class Simulated: SGCManager {
|
|
|
133
133
|
Bridge.log("clearDisplay")
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
func sendTextWall(_: String) {
|
|
136
|
+
func sendTextWall(_: String) async {
|
|
137
137
|
Bridge.log("sendTextWall")
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
func sendDoubleTextWall(_: String, _: String) {
|
|
140
|
+
func sendDoubleTextWall(_: String, _: String) async {
|
|
141
141
|
Bridge.log("sendDoubleTextWall")
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -3,7 +3,11 @@ import Foundation
|
|
|
3
3
|
public struct MentraBluetoothSDKConfiguration {
|
|
4
4
|
public static let `default` = MentraBluetoothSDKConfiguration()
|
|
5
5
|
|
|
6
|
-
public
|
|
6
|
+
public let analytics: BluetoothSdkAnalyticsConfiguration
|
|
7
|
+
|
|
8
|
+
public init(analytics: BluetoothSdkAnalyticsConfiguration = BluetoothSdkAnalyticsConfiguration()) {
|
|
9
|
+
self.analytics = analytics
|
|
10
|
+
}
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
public enum DeviceModel: String {
|
package/package.json
CHANGED
package/plugin/build/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type ConfigPlugin } from "expo/config-plugins";
|
|
2
2
|
export interface BluetoothSdkPluginProps {
|
|
3
3
|
node?: boolean;
|
|
4
|
+
analytics?: boolean | BluetoothSdkAnalyticsPluginProps;
|
|
5
|
+
}
|
|
6
|
+
export interface BluetoothSdkAnalyticsPluginProps {
|
|
7
|
+
enabled?: boolean;
|
|
4
8
|
}
|
|
5
9
|
declare const withBluetoothSdk: ConfigPlugin<BluetoothSdkPluginProps>;
|
|
6
10
|
export default withBluetoothSdk;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { type ConfigPlugin } from "expo/config-plugins";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}>;
|
|
2
|
+
import { type BluetoothSdkPluginProps } from "./index";
|
|
3
|
+
export declare const withAndroidConfiguration: ConfigPlugin<BluetoothSdkPluginProps>;
|
|
@@ -7,6 +7,10 @@ exports.withAndroidConfiguration = void 0;
|
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const config_plugins_1 = require("expo/config-plugins");
|
|
10
|
+
const META_ANALYTICS_DISABLED = "com.mentra.bluetoothsdk.analytics.disabled";
|
|
11
|
+
const STALE_META_POSTHOG_API_KEY = "com.mentra.bluetoothsdk.analytics.posthog_api_key";
|
|
12
|
+
const STALE_META_POSTHOG_HOST = "com.mentra.bluetoothsdk.analytics.posthog_host";
|
|
13
|
+
const ANALYTICS_META_NAMES = [META_ANALYTICS_DISABLED, STALE_META_POSTHOG_API_KEY, STALE_META_POSTHOG_HOST];
|
|
10
14
|
function getBluetoothSdkRoot() {
|
|
11
15
|
return path_1.default.dirname(require.resolve("../../package.json"));
|
|
12
16
|
}
|
|
@@ -133,9 +137,51 @@ function withSherpaOnnxLocalMavenRepo(config) {
|
|
|
133
137
|
return config;
|
|
134
138
|
});
|
|
135
139
|
}
|
|
140
|
+
function resolveAnalyticsProps(props) {
|
|
141
|
+
const analytics = props?.analytics;
|
|
142
|
+
let disabled;
|
|
143
|
+
if (analytics === false) {
|
|
144
|
+
disabled = true;
|
|
145
|
+
}
|
|
146
|
+
else if (analytics === true) {
|
|
147
|
+
disabled = false;
|
|
148
|
+
}
|
|
149
|
+
else if (typeof analytics === "object" && analytics.enabled !== undefined) {
|
|
150
|
+
disabled = !analytics.enabled;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
disabled,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function upsertMetaData(application, name, value) {
|
|
157
|
+
application["meta-data"] ??= [];
|
|
158
|
+
const existing = application["meta-data"].find((item) => item.$?.["android:name"] === name);
|
|
159
|
+
const entry = existing ?? { $: { "android:name": name } };
|
|
160
|
+
entry.$["android:value"] = value;
|
|
161
|
+
if (!existing) {
|
|
162
|
+
application["meta-data"].push(entry);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function removeMetaData(application, name) {
|
|
166
|
+
application["meta-data"] = (application["meta-data"] ?? []).filter((item) => item.$?.["android:name"] !== name);
|
|
167
|
+
}
|
|
168
|
+
function withAnalyticsManifestMetadata(config, props) {
|
|
169
|
+
return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
170
|
+
const analytics = resolveAnalyticsProps(props);
|
|
171
|
+
const application = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
|
|
172
|
+
for (const name of ANALYTICS_META_NAMES) {
|
|
173
|
+
removeMetaData(application, name);
|
|
174
|
+
}
|
|
175
|
+
if (analytics.disabled !== undefined) {
|
|
176
|
+
upsertMetaData(application, META_ANALYTICS_DISABLED, analytics.disabled ? "true" : "false");
|
|
177
|
+
}
|
|
178
|
+
return config;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
136
181
|
const withAndroidConfiguration = (config, props) => {
|
|
137
182
|
config = withSettingsGradleModifications(config);
|
|
138
183
|
config = withSherpaOnnxLocalMavenRepo(config);
|
|
184
|
+
config = withAnalyticsManifestMetadata(config, props);
|
|
139
185
|
if (props?.node) {
|
|
140
186
|
config = withGradlePropertiesModifications(config);
|
|
141
187
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { type ConfigPlugin } from "expo/config-plugins";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}>;
|
|
2
|
+
import { type BluetoothSdkPluginProps } from "./index";
|
|
3
|
+
export declare const withIosConfiguration: ConfigPlugin<BluetoothSdkPluginProps>;
|
package/plugin/build/withIos.js
CHANGED
|
@@ -10,6 +10,9 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const config_plugins_1 = require("expo/config-plugins");
|
|
11
11
|
const BLUETOOTH_SDK_EXPO_ADAPTER_ENV = "MENTRA_BLUETOOTH_SDK_INCLUDE_EXPO_ADAPTER";
|
|
12
12
|
const BLUETOOTH_SDK_EXPO_ADAPTER_LINE = `ENV['${BLUETOOTH_SDK_EXPO_ADAPTER_ENV}'] ||= '1'`;
|
|
13
|
+
const INFO_ANALYTICS_DISABLED = "MentraBluetoothSdkAnalyticsDisabled";
|
|
14
|
+
const STALE_INFO_POSTHOG_API_KEY = "MentraBluetoothSdkPostHogApiKey";
|
|
15
|
+
const STALE_INFO_POSTHOG_HOST = "MentraBluetoothSdkPostHogHost";
|
|
13
16
|
const ensureBluetoothSdkExpoAdapterPodEnv = (podfile) => {
|
|
14
17
|
if (podfile.includes(BLUETOOTH_SDK_EXPO_ADAPTER_ENV)) {
|
|
15
18
|
return podfile;
|
|
@@ -51,11 +54,40 @@ const withXcodeEnvLocal = (config) => {
|
|
|
51
54
|
},
|
|
52
55
|
]);
|
|
53
56
|
};
|
|
57
|
+
function resolveAnalyticsProps(props) {
|
|
58
|
+
const analytics = props?.analytics;
|
|
59
|
+
let disabled;
|
|
60
|
+
if (analytics === false) {
|
|
61
|
+
disabled = true;
|
|
62
|
+
}
|
|
63
|
+
else if (analytics === true) {
|
|
64
|
+
disabled = false;
|
|
65
|
+
}
|
|
66
|
+
else if (typeof analytics === "object" && analytics.enabled !== undefined) {
|
|
67
|
+
disabled = !analytics.enabled;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
disabled,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function withAnalyticsInfoPlist(config, props) {
|
|
74
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
75
|
+
const analytics = resolveAnalyticsProps(props);
|
|
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
|
+
}
|
|
82
|
+
return config;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
54
85
|
const withIosConfiguration = (config, props) => {
|
|
55
86
|
config = (0, config_plugins_1.withPodfile)(config, (config) => {
|
|
56
87
|
config.modResults.contents = ensureBluetoothSdkExpoAdapterPodEnv(config.modResults.contents);
|
|
57
88
|
return config;
|
|
58
89
|
});
|
|
90
|
+
config = withAnalyticsInfoPlist(config, props);
|
|
59
91
|
if (props?.node) {
|
|
60
92
|
config = withXcodeEnvLocal(config);
|
|
61
93
|
}
|
|
@@ -21,6 +21,14 @@ export type TouchEvent = {
|
|
|
21
21
|
timestamp: number
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export type AccelEvent = {
|
|
25
|
+
type: "accel_event"
|
|
26
|
+
x: number
|
|
27
|
+
y: number
|
|
28
|
+
z: number
|
|
29
|
+
timestamp: number
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
export type HeadUpEvent = {
|
|
25
33
|
up: boolean
|
|
26
34
|
}
|
|
@@ -299,6 +307,24 @@ export type VideoRecordingSuccessStatusEvent =
|
|
|
299
307
|
| VideoRecordingStartedStatusEvent
|
|
300
308
|
| VideoRecordingStoppedStatusEvent
|
|
301
309
|
|
|
310
|
+
export type MediaUploadSuccessEvent = {
|
|
311
|
+
type: "media_success"
|
|
312
|
+
requestId: string
|
|
313
|
+
mediaUrl: string
|
|
314
|
+
mediaType: number
|
|
315
|
+
timestamp: number
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export type MediaUploadErrorEvent = {
|
|
319
|
+
type: "media_error"
|
|
320
|
+
requestId: string
|
|
321
|
+
errorMessage: string
|
|
322
|
+
mediaType: number
|
|
323
|
+
timestamp: number
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export type MediaUploadEvent = MediaUploadSuccessEvent | MediaUploadErrorEvent
|
|
327
|
+
|
|
302
328
|
export type GalleryStatusEvent = {
|
|
303
329
|
type: "gallery_status"
|
|
304
330
|
photos: number
|
|
@@ -396,7 +422,7 @@ export type SettingsAckSuccessEvent = Omit<SettingsAckEvent, "status"> & {
|
|
|
396
422
|
export type RgbLedAction = "on" | "off"
|
|
397
423
|
export type RgbLedColor = "red" | "green" | "blue" | "orange" | "white"
|
|
398
424
|
export type PhotoSize = "small" | "medium" | "large" | "full"
|
|
399
|
-
export type ButtonPhotoSize = "small" | "medium" | "large"
|
|
425
|
+
export type ButtonPhotoSize = "small" | "medium" | "large" | "max"
|
|
400
426
|
export type PhotoCompression = "none" | "medium" | "heavy"
|
|
401
427
|
|
|
402
428
|
/**
|
|
@@ -408,6 +434,12 @@ export interface VideoRecordingSettings {
|
|
|
408
434
|
width?: number
|
|
409
435
|
height?: number
|
|
410
436
|
fps?: number
|
|
437
|
+
/**
|
|
438
|
+
* Optional auto-stop timer in minutes, sent on `start_video_recording`.
|
|
439
|
+
* `0` (the default) means record until stopped or interrupted
|
|
440
|
+
* (battery/storage/thermal/error).
|
|
441
|
+
*/
|
|
442
|
+
maxRecordingTimeMinutes?: number
|
|
411
443
|
}
|
|
412
444
|
export const DeviceModels = {
|
|
413
445
|
Simulated: "Simulated Glasses",
|
|
@@ -722,6 +754,7 @@ export type BluetoothSdkModuleEvents = {
|
|
|
722
754
|
glasses_not_ready: (event: GlassesNotReadyEvent) => void
|
|
723
755
|
button_press: (event: ButtonPressEvent) => void
|
|
724
756
|
touch_event: (event: TouchEvent) => void
|
|
757
|
+
accel_event: (event: AccelEvent) => void
|
|
725
758
|
head_up: (event: HeadUpEvent) => void
|
|
726
759
|
voice_activity_detection_status: (event: VoiceActivityDetectionStatusEvent) => void
|
|
727
760
|
speaking_status: (event: SpeakingStatusEvent) => void
|
|
@@ -734,6 +767,8 @@ export type BluetoothSdkModuleEvents = {
|
|
|
734
767
|
photo_response: (event: PhotoResponseEvent) => void
|
|
735
768
|
photo_status: (event: PhotoStatusEvent) => void
|
|
736
769
|
video_recording_status: (event: VideoRecordingStatusEvent) => void
|
|
770
|
+
media_success: (event: MediaUploadSuccessEvent) => void
|
|
771
|
+
media_error: (event: MediaUploadErrorEvent) => void
|
|
737
772
|
gallery_status: (event: GalleryStatusEvent) => void
|
|
738
773
|
compatible_glasses_search_stop: (event: CompatibleGlassesSearchStopEvent) => void
|
|
739
774
|
heartbeat_sent: (event: HeartbeatSentEvent) => void
|
|
@@ -796,6 +831,7 @@ export type BluetoothSdkEventMap = {
|
|
|
796
831
|
glasses_not_ready: GlassesNotReadyEvent
|
|
797
832
|
button_press: ButtonPressEvent
|
|
798
833
|
touch_event: TouchEvent
|
|
834
|
+
accel_event: AccelEvent
|
|
799
835
|
head_up: HeadUpEvent
|
|
800
836
|
voice_activity_detection_status: VoiceActivityDetectionStatusEvent
|
|
801
837
|
speaking_status: SpeakingStatusEvent
|
|
@@ -808,6 +844,8 @@ export type BluetoothSdkEventMap = {
|
|
|
808
844
|
photo_response: PhotoResponseEvent
|
|
809
845
|
photo_status: PhotoStatusEvent
|
|
810
846
|
video_recording_status: VideoRecordingStatusEvent
|
|
847
|
+
media_success: MediaUploadSuccessEvent
|
|
848
|
+
media_error: MediaUploadErrorEvent
|
|
811
849
|
gallery_status: GalleryStatusEvent
|
|
812
850
|
compatible_glasses_search_stop: CompatibleGlassesSearchStopEvent
|
|
813
851
|
swipe_volume_status: SwipeVolumeStatusEvent
|
|
@@ -865,6 +903,7 @@ export interface BluetoothSdkPublicModule {
|
|
|
865
903
|
showDashboard(): Promise<void>
|
|
866
904
|
setDashboardPosition(height: number, depth: number): Promise<void>
|
|
867
905
|
setHeadUpAngle(angleDegrees: number): Promise<void>
|
|
906
|
+
setImuEnabled(enabled: boolean): Promise<void>
|
|
868
907
|
setScreenDisabled(disabled: boolean): Promise<void>
|
|
869
908
|
|
|
870
909
|
requestWifiScan(): Promise<WifiSearchResult[]>
|
|
@@ -887,7 +926,18 @@ export interface BluetoothSdkPublicModule {
|
|
|
887
926
|
sound: boolean,
|
|
888
927
|
settings?: VideoRecordingSettings,
|
|
889
928
|
): Promise<VideoRecordingStartedStatusEvent>
|
|
890
|
-
|
|
929
|
+
/**
|
|
930
|
+
* Stop the active recording. When {@link webhookUrl} is provided, the glasses
|
|
931
|
+
* upload the recorded video to it (multipart) using {@link authToken}. These
|
|
932
|
+
* are supplied at stop time (not start) so the token is fresh when the upload
|
|
933
|
+
* runs — a recording can last arbitrarily long. An empty/omitted webhook keeps
|
|
934
|
+
* the video on device (no upload).
|
|
935
|
+
*/
|
|
936
|
+
stopVideoRecording(
|
|
937
|
+
requestId: string,
|
|
938
|
+
webhookUrl?: string,
|
|
939
|
+
authToken?: string,
|
|
940
|
+
): Promise<VideoRecordingStoppedStatusEvent>
|
|
891
941
|
|
|
892
942
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
893
943
|
stopStream(): Promise<StreamStatusEvent>
|
|
@@ -1140,6 +1190,7 @@ export type BluetoothSettingsUpdate = Partial<{
|
|
|
1140
1190
|
screen_disabled: boolean
|
|
1141
1191
|
contextual_dashboard: boolean
|
|
1142
1192
|
head_up_angle: number
|
|
1193
|
+
imu_enabled: boolean
|
|
1143
1194
|
brightness: number
|
|
1144
1195
|
auto_brightness: boolean
|
|
1145
1196
|
dashboard_height: number
|
|
@@ -100,6 +100,7 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
100
100
|
setDashboardMenu(items: DashboardMenuItem[]): Promise<void>
|
|
101
101
|
setCalendarEvents(events: CalendarEvent[]): Promise<void>
|
|
102
102
|
setHeadUpAngle(angleDegrees: number): Promise<void>
|
|
103
|
+
setImuEnabled(enabled: boolean): Promise<void>
|
|
103
104
|
setScreenDisabled(disabled: boolean): Promise<void>
|
|
104
105
|
ping(): Promise<void>
|
|
105
106
|
dbg1(): Promise<void>
|
|
@@ -147,7 +148,11 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
147
148
|
sound: boolean,
|
|
148
149
|
settings?: VideoRecordingSettings,
|
|
149
150
|
): Promise<VideoRecordingStartedStatusEvent>
|
|
150
|
-
stopVideoRecording(
|
|
151
|
+
stopVideoRecording(
|
|
152
|
+
requestId: string,
|
|
153
|
+
webhookUrl?: string,
|
|
154
|
+
authToken?: string,
|
|
155
|
+
): Promise<VideoRecordingStoppedStatusEvent>
|
|
151
156
|
|
|
152
157
|
// Stream Commands
|
|
153
158
|
startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
|
|
@@ -225,6 +230,20 @@ const DEFAULT_CONNECT_OPTIONS: Required<ConnectOptions> = {
|
|
|
225
230
|
|
|
226
231
|
const DEFAULT_SCAN_TIMEOUT_MS = 15_000
|
|
227
232
|
|
|
233
|
+
function bindNativeMethod<T extends (...args: never[]) => unknown>(
|
|
234
|
+
module: Record<string, unknown>,
|
|
235
|
+
name: string,
|
|
236
|
+
): T {
|
|
237
|
+
const method = module[name]
|
|
238
|
+
if (typeof method !== "function") {
|
|
239
|
+
console.warn(`[BluetoothSdk] Native method "${name}" is unavailable — rebuild the app (bun android / bun ios)`)
|
|
240
|
+
return (async () => {
|
|
241
|
+
throw new Error(`BluetoothSdk.${name} is not available in this native build. Rebuild the app.`)
|
|
242
|
+
}) as T
|
|
243
|
+
}
|
|
244
|
+
return method.bind(module) as T
|
|
245
|
+
}
|
|
246
|
+
|
|
228
247
|
const CAMERA_ROI_MIN = 0
|
|
229
248
|
const CAMERA_ROI_MAX = 2
|
|
230
249
|
const CAMERA_ROI_POSITION_VALUES: Record<CameraRoiPosition, CameraFovSetting["roiPosition"]> = {
|
|
@@ -425,6 +444,10 @@ NativeBluetoothSdkModule.setHeadUpAngle = function (angleDegrees: number) {
|
|
|
425
444
|
return this.updateBluetoothSettings({head_up_angle: angleDegrees})
|
|
426
445
|
}
|
|
427
446
|
|
|
447
|
+
NativeBluetoothSdkModule.setImuEnabled = function (enabled: boolean) {
|
|
448
|
+
return this.updateBluetoothSettings({imu_enabled: enabled})
|
|
449
|
+
}
|
|
450
|
+
|
|
428
451
|
NativeBluetoothSdkModule.setScreenDisabled = function (disabled: boolean) {
|
|
429
452
|
return this.updateBluetoothSettings({screen_disabled: disabled})
|
|
430
453
|
}
|
|
@@ -433,9 +456,9 @@ NativeBluetoothSdkModule.setVoiceActivityDetectionEnabled = function (enabled: b
|
|
|
433
456
|
return this.updateBluetoothSettings({voice_activity_detection_enabled: enabled})
|
|
434
457
|
}
|
|
435
458
|
|
|
436
|
-
const nativeSetCameraFov =
|
|
437
|
-
fov: CameraFovSetting
|
|
438
|
-
|
|
459
|
+
const nativeSetCameraFov = bindNativeMethod<
|
|
460
|
+
(fov: CameraFovSetting) => MaybePromise<CameraFovResult>
|
|
461
|
+
>(NativeBluetoothSdkModule as unknown as Record<string, unknown>, "setCameraFov")
|
|
439
462
|
NativeBluetoothSdkModule.setCameraFov = function (request: CameraFovRequest) {
|
|
440
463
|
const setting = normalizeCameraFov(request)
|
|
441
464
|
return Promise.resolve(nativeSetCameraFov(setting))
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
|
|
|
8
8
|
"glasses_not_ready",
|
|
9
9
|
"button_press",
|
|
10
10
|
"touch_event",
|
|
11
|
+
"accel_event",
|
|
11
12
|
"head_up",
|
|
12
13
|
"voice_activity_detection_status",
|
|
13
14
|
"speaking_status",
|
|
@@ -20,6 +21,8 @@ const PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([
|
|
|
20
21
|
"photo_response",
|
|
21
22
|
"photo_status",
|
|
22
23
|
"video_recording_status",
|
|
24
|
+
"media_success",
|
|
25
|
+
"media_error",
|
|
23
26
|
"gallery_status",
|
|
24
27
|
"compatible_glasses_search_stop",
|
|
25
28
|
"swipe_volume_status",
|
|
@@ -65,6 +68,7 @@ export const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({
|
|
|
65
68
|
showDashboard: PrivateBluetoothSdkModule.showDashboard.bind(PrivateBluetoothSdkModule),
|
|
66
69
|
setDashboardPosition: PrivateBluetoothSdkModule.setDashboardPosition.bind(PrivateBluetoothSdkModule),
|
|
67
70
|
setHeadUpAngle: PrivateBluetoothSdkModule.setHeadUpAngle.bind(PrivateBluetoothSdkModule),
|
|
71
|
+
setImuEnabled: PrivateBluetoothSdkModule.setImuEnabled.bind(PrivateBluetoothSdkModule),
|
|
68
72
|
setScreenDisabled: PrivateBluetoothSdkModule.setScreenDisabled.bind(PrivateBluetoothSdkModule),
|
|
69
73
|
requestWifiScan: PrivateBluetoothSdkModule.requestWifiScan.bind(PrivateBluetoothSdkModule),
|
|
70
74
|
sendWifiCredentials: PrivateBluetoothSdkModule.sendWifiCredentials.bind(PrivateBluetoothSdkModule),
|
|
@@ -124,6 +128,7 @@ export {
|
|
|
124
128
|
} from "./BluetoothSdk.types"
|
|
125
129
|
|
|
126
130
|
export type {
|
|
131
|
+
AccelEvent,
|
|
127
132
|
AudioConnectedEvent,
|
|
128
133
|
AudioDisconnectedEvent,
|
|
129
134
|
AudioPairingNeededEvent,
|