@glassly/engine 0.3.5-dev.841 → 0.3.5-dev.843
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/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +34 -30
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +3 -2
- package/build/facades/pairing.js.map +1 -1
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/ScreenshotFixture.d.ts +56 -0
- package/build/services/ScreenshotFixture.d.ts.map +1 -0
- package/build/services/ScreenshotFixture.js +72 -0
- package/build/services/ScreenshotFixture.js.map +1 -0
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +3 -0
- package/build/stores/settings.js.map +1 -1
- package/package.json +7 -7
- package/src/facades/glasses.ts +58 -42
- package/src/facades/pairing.ts +4 -2
- package/src/generated/releaseMetadata.ts +3 -3
- package/src/services/ScreenshotFixture.ts +113 -0
- package/src/stores/settings.ts +3 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,MAAM,EACN,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACX,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,MAAM,EACN,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACX,MAAM,wBAAwB,CAAA;AAgB/B,OAAwB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EACjC,MAAM,wCAAwC,CAAA;AAW/C,iBAAS,aAAa;;;;;;;;;;;;;;;;EAkBrB;AAsCD,iBAAS,qBAAqB;IAG1B,sFAAsF;;;;IAItF,6DAA6D;;;;EAKhE;AAED,iBAAS,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGnB;AA4BD,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;AACpE,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAC7E,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAA;AAEhE,eAAO,MAAM,OAAO;IAElB;;;;OAIG;0BACuB,OAAO,CAAC,IAAI,CAAC;sBASvB,OAAO,CAAC,IAAI,CAAC;kBACjB,OAAO,CAAC,IAAI,CAAC;IACzB,iDAAiD;sBACzB,MAAM,YAAY,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IASxE,4DAA4D;4BACtC,OAAO,CAAC,IAAI,CAAC;IACnC,qDAAqD;yBAChC,MAAM,GAAG,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;IAClD;;;;;;OAMG;qBACkB,OAAO,CAAC,OAAO,CAAC;IAcrC;;;iEAG6D;0BACzC,OAAO;IAC3B;;;;;;;;;;OAUG;4BACmB,OAAO,CAAC,OAAO,CAAC;kBAG1B,qBAAqB;mBAClB,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;gBAC3D,mBAAmB;iBAChB,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;IAE/D,iFAAiF;;wBAI7D,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;yBAI/C,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;QAO7D,2FAA2F;oBACjF,OAAO,CAAC,mBAAmB,EAAE,CAAC;QACxC,oFAAoF;0BAC5D;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAC,KAAG,OAAO,CAAC,IAAI,CAAC;yBAI7E,OAAO,CAAC,IAAI,CAAC;0BACZ,OAAO,CAAC,IAAI,CAAC;sBACX,OAAO,CAAC,IAAI,CAAC;uBAIhB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;0BACnB,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;sBAC7B,sBAAsB;uBACnB,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;uBAUvD,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;wBAInD,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;;QAQrE,2CAA2C;oBACjC,gBAAgB,EAAE;qBACf,CAAC,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;QAUrE,gFAAgF;wBAClE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;qBACjC,MAAM,KAAG,gBAAgB,GAAG,SAAS;uBACnC,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;QAElF,kEAAkE;yBACnD,gBAAgB,EAAE;8BACX,MAAM,KAAG,OAAO;sBAIxB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;QACpC,+DAA+D;0BAC/C,OAAO,CAAC,IAAI,CAAC;QAC7B,kFAAkF;6BAC/D,OAAO,CAAC,IAAI,CAAC;QAChC,+CAA+C;yBAC9B,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;qBACpB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;;;QAQzC,kFAAkF;sCACpD,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;gCAE9C,OAAO,CAAC,IAAI,CAAC;8BACf,OAAO,CAAC,OAAO,CAAC;QACpC,sFAAsF;wBACtE,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;QACzC,4FAA4F;2BAC3E,qBAAqB;0BACtB,IAAI;4BACA,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;QAC1E,oFAAoF;6BAC/D,MAAM,WAAW,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;gCACrC,MAAM,KAAG,2BAA2B;kCAClC,CAAC,MAAM,EAAE,2BAA2B,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;QAEtF,6EAA6E;yBAC5D,MAAM,WAAW,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;sBAC3C,MAAM,QAAQ,MAAM,GAAG,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;;;QAK5D,iFAAiF;4BAC/D,OAAO,CAAC,sBAAsB,CAAC;;;QAKjD,8CAA8C;;QAE9C,qCAAqC;gCACb,MAAM;QAC9B,6EAA6E;oCACjD,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;QACnD;;;;WAIG;yBACc,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;;;;;;;;;;;;;;;;CAgBzD,CAAA"}
|
package/build/facades/glasses.js
CHANGED
|
@@ -16,6 +16,7 @@ import { useSettingsStore, SETTINGS } from "../stores/settings";
|
|
|
16
16
|
import { hasDefaultDevice } from "../services/DeviceStoreHydration";
|
|
17
17
|
import micStateCoordinator from "../services/MicStateCoordinator";
|
|
18
18
|
import { projectPairingIdentity } from "../services/PairingIdentity";
|
|
19
|
+
import { fixtureControllerList, fixtureControllerStatuses, fixtureInfo, fixtureStatus, isScreenshotMode, } from "../services/ScreenshotFixture";
|
|
19
20
|
import { isGlassesConnected, isGlassesReady } from "../services/GlassesReadiness";
|
|
20
21
|
import { pushAllBluetoothSettings } from "../services/GlassesSettingsSync";
|
|
21
22
|
import { isControllerReady, pairedControllersFrom, readyControllers } from "../services/controllers";
|
|
@@ -26,9 +27,11 @@ import { pcmLevelDbfs } from "../utils/audio/pcmLevel";
|
|
|
26
27
|
import { glassesWifi } from "./glassesWifi";
|
|
27
28
|
import { glassesSettings } from "./glassesSettings";
|
|
28
29
|
let micLevelSubscriptions = 0;
|
|
30
|
+
// The screenshot fixture (ScreenshotFixture.ts) dresses these read-models
|
|
31
|
+
// while the identity is the simulated device; it is a no-op outside that build.
|
|
29
32
|
function projectStatus() {
|
|
30
33
|
const s = useGlassesStore.getState();
|
|
31
|
-
return {
|
|
34
|
+
return fixtureStatus({
|
|
32
35
|
state: s.connection.state,
|
|
33
36
|
fullyBooted: isGlassesReady(s.connection),
|
|
34
37
|
battery: s.batteryLevel,
|
|
@@ -40,10 +43,10 @@ function projectStatus() {
|
|
|
40
43
|
btClassic: s.bluetoothClassicConnected,
|
|
41
44
|
// iOS: glasses may read the phone's ANCS stream ("Share System Notifications").
|
|
42
45
|
ancsAuthorized: Boolean(s.ancsAuthorized),
|
|
43
|
-
};
|
|
46
|
+
}, projectPairingIdentity());
|
|
44
47
|
}
|
|
45
48
|
function projectControllerList() {
|
|
46
|
-
return pairedControllersFrom(useSettingsStore.getState().getSetting(SETTINGS.controllers.key));
|
|
49
|
+
return fixtureControllerList(pairedControllersFrom(useSettingsStore.getState().getSetting(SETTINGS.controllers.key)), projectPairingIdentity());
|
|
47
50
|
}
|
|
48
51
|
// Copies: snapshots must not hand callers mutable references into the store.
|
|
49
52
|
function projectControllerStatuses() {
|
|
@@ -51,7 +54,27 @@ function projectControllerStatuses() {
|
|
|
51
54
|
for (const [id, status] of Object.entries(useGlassesStore.getState().controllers ?? {})) {
|
|
52
55
|
statuses[id] = { ...status };
|
|
53
56
|
}
|
|
54
|
-
return statuses;
|
|
57
|
+
return fixtureControllerStatuses(statuses, projectPairingIdentity());
|
|
58
|
+
}
|
|
59
|
+
// Dedupe on the projected snapshot: the store updates on many fields; fire
|
|
60
|
+
// only when the projection changes. The fixture's read-models also follow the
|
|
61
|
+
// identity (a settings key), so that build watches the settings store too.
|
|
62
|
+
function subscribeProjection(project, cb) {
|
|
63
|
+
let last = JSON.stringify(project());
|
|
64
|
+
const notify = () => {
|
|
65
|
+
const snap = project();
|
|
66
|
+
const key = JSON.stringify(snap);
|
|
67
|
+
if (key === last)
|
|
68
|
+
return;
|
|
69
|
+
last = key;
|
|
70
|
+
cb(snap);
|
|
71
|
+
};
|
|
72
|
+
const unsubscribeGlasses = useGlassesStore.subscribe(notify);
|
|
73
|
+
const unsubscribeSettings = isScreenshotMode() ? useSettingsStore.subscribe(notify) : () => { };
|
|
74
|
+
return () => {
|
|
75
|
+
unsubscribeGlasses();
|
|
76
|
+
unsubscribeSettings();
|
|
77
|
+
};
|
|
55
78
|
}
|
|
56
79
|
function projectKeyboardStatus() {
|
|
57
80
|
const s = useGlassesStore.getState();
|
|
@@ -68,6 +91,9 @@ function projectKeyboardStatus() {
|
|
|
68
91
|
}
|
|
69
92
|
function projectInfo() {
|
|
70
93
|
const s = useGlassesStore.getState();
|
|
94
|
+
return fixtureInfo(projectRawInfo(s), projectPairingIdentity());
|
|
95
|
+
}
|
|
96
|
+
function projectRawInfo(s) {
|
|
71
97
|
return {
|
|
72
98
|
model: s.deviceModel,
|
|
73
99
|
style: s.style,
|
|
@@ -169,31 +195,9 @@ export const glasses = {
|
|
|
169
195
|
hasDefaultDevice: () => hasDefaultDevice(),
|
|
170
196
|
// --- read-model (projected from the engine-owned glasses store) ---
|
|
171
197
|
status: () => projectStatus(),
|
|
172
|
-
onStatus: (cb) =>
|
|
173
|
-
// Dedupe: the glasses store updates on many fields; only fire when the
|
|
174
|
-
// projected status snapshot actually changes.
|
|
175
|
-
let last = JSON.stringify(projectStatus());
|
|
176
|
-
return useGlassesStore.subscribe(() => {
|
|
177
|
-
const snap = projectStatus();
|
|
178
|
-
const key = JSON.stringify(snap);
|
|
179
|
-
if (key === last)
|
|
180
|
-
return;
|
|
181
|
-
last = key;
|
|
182
|
-
cb(snap);
|
|
183
|
-
});
|
|
184
|
-
},
|
|
198
|
+
onStatus: (cb) => subscribeProjection(projectStatus, cb),
|
|
185
199
|
info: () => projectInfo(),
|
|
186
|
-
onInfo: (cb) =>
|
|
187
|
-
let last = JSON.stringify(projectInfo());
|
|
188
|
-
return useGlassesStore.subscribe(() => {
|
|
189
|
-
const snap = projectInfo();
|
|
190
|
-
const key = JSON.stringify(snap);
|
|
191
|
-
if (key === last)
|
|
192
|
-
return;
|
|
193
|
-
last = key;
|
|
194
|
-
cb(snap);
|
|
195
|
-
});
|
|
196
|
-
},
|
|
200
|
+
onInfo: (cb) => subscribeProjection(projectInfo, cb),
|
|
197
201
|
capabilities: () => getModelCapabilities(useGlassesStore.getState().deviceModel),
|
|
198
202
|
/** Ask the glasses to report fresh firmware/version info (updates the store). */
|
|
199
203
|
requestVersionInfo: () => BluetoothSdk.requestVersionInfo(),
|
|
@@ -262,10 +266,10 @@ export const glasses = {
|
|
|
262
266
|
/** Live driver statuses by id; a paired controller without an entry is idle. */
|
|
263
267
|
statuses: () => projectControllerStatuses(),
|
|
264
268
|
status: (id) => projectControllerStatuses()[id],
|
|
265
|
-
onStatus: (cb) =>
|
|
269
|
+
onStatus: (cb) => subscribeProjection(projectControllerStatuses, cb),
|
|
266
270
|
/** Controllers delivering input (linked, route or claim done). */
|
|
267
271
|
connected: () => readyControllers(projectControllerStatuses()),
|
|
268
|
-
isConnected: (model) => Object.values(
|
|
272
|
+
isConnected: (model) => Object.values(projectControllerStatuses()).some((status) => isControllerReady(status) && (model === undefined || status.model === model)),
|
|
269
273
|
connect: (id) => BluetoothSdk.connectController(id),
|
|
270
274
|
/** Reconnect every paired controller that is not connected. */
|
|
271
275
|
connectAll: () => BluetoothSdk.connectControllers(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * glasses facade — the core `engine.glasses.*` surface: connection actions (over\n * the bluetooth-sdk passthrough), a curated status/info read-model (projected from\n * the engine-owned glasses store), capabilities (from the model table), and the\n * discrete input events. The `wifi` sub-facade is nested here.\n *\n * Read-models project the raw glasses store into the stable shape OEM UIs consume,\n * so the device store's field layout doesn't leak into every screen.\n */\n// Internal btsdk surface — connectSimulated/reconnect/controller live on the full\n// surface, not the public entry. Relative path (the alias doesn't resolve in engine's\n// standalone build); jest moduleNameMapper + tsconfig both resolve it.\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {\n AppleWatchAvailability,\n ButtonPressEvent,\n ConnectOptions,\n ControllerStatus,\n Device,\n KeyboardAdvertEvent,\n KeyboardPairingEvent,\n PairedController,\n TouchEvent,\n} from \"@glassly/bluetooth-sdk\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {hasDefaultDevice} from \"../services/DeviceStoreHydration\"\nimport micStateCoordinator from \"../services/MicStateCoordinator\"\nimport {projectPairingIdentity} from \"../services/PairingIdentity\"\nimport {isGlassesConnected, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {pushAllBluetoothSettings} from \"../services/GlassesSettingsSync\"\nimport {isControllerReady, pairedControllersFrom, readyControllers} from \"../services/controllers\"\nimport neuralBandInput, {\n type NeuralBandClaimStatus,\n type NeuralBandHandwritingStatus,\n type NeuralBandHandwritingUpdate,\n} from \"../services/neuralBand/NeuralBandInput\"\nimport {recordSupportProfileConnectionFailure} from \"../services/SupportProfileSync\"\nimport {getModelCapabilities, type DeviceTypes} from \"../types\"\nimport {pcmLevelDbfs} from \"../utils/audio/pcmLevel\"\nimport {glassesWifi} from \"./glassesWifi\"\nimport {glassesSettings} from \"./glassesSettings\"\n\nlet micLevelSubscriptions = 0\n\nfunction projectStatus() {\n const s = useGlassesStore.getState()\n return {\n state: s.connection.state,\n fullyBooted: isGlassesReady(s.connection),\n battery: s.batteryLevel,\n charging: s.charging,\n case: {battery: s.caseBatteryLevel, charging: s.caseCharging, open: s.caseOpen, removed: s.caseRemoved},\n signal: s.signalStrength,\n micEnabled: s.micEnabled,\n vadEnabled: s.voiceActivityDetectionEnabled,\n btClassic: s.bluetoothClassicConnected,\n // iOS: glasses may read the phone's ANCS stream (\"Share System Notifications\").\n ancsAuthorized: Boolean(s.ancsAuthorized),\n }\n}\n\nfunction projectControllerList(): PairedController[] {\n return pairedControllersFrom(useSettingsStore.getState().getSetting(SETTINGS.controllers.key))\n}\n\n// Copies: snapshots must not hand callers mutable references into the store.\nfunction projectControllerStatuses(): Record<string, ControllerStatus> {\n const statuses: Record<string, ControllerStatus> = {}\n for (const [id, status] of Object.entries(useGlassesStore.getState().controllers ?? {})) {\n statuses[id] = {...status}\n }\n return statuses\n}\n\nfunction projectKeyboardStatus() {\n const s = useGlassesStore.getState()\n return {\n /** Connected glasses run a firmware that can host a keyboard (G2 Glassly CFW 39+). */\n available: s.keyboardBridge === true,\n connected: s.keyboardConnected === true,\n name: s.keyboardName ?? \"\",\n /** A keyboard is remembered and reconnects automatically. */\n saved: s.keyboardSaved === true,\n state: s.keyboardState ?? \"idle\",\n error: s.keyboardError ?? \"\",\n }\n}\n\nfunction projectInfo() {\n const s = useGlassesStore.getState()\n return {\n model: s.deviceModel,\n style: s.style,\n color: s.color,\n bluetoothName: s.bluetoothName,\n firmwareVersion: s.firmwareVersion,\n mtkFirmware: s.mtkFirmwareVersion,\n besFirmware: s.besFirmwareVersion,\n appVersion: s.appVersion,\n androidVersion: s.androidVersion,\n serialNumber: s.serialNumber,\n buildNumber: s.buildNumber,\n btMac: s.bluetoothMacAddress,\n wifiMac: s.wifiMacAddress,\n leftMac: s.leftMacAddress,\n rightMac: s.rightMacAddress,\n sceneCanvasWidth: s.sceneCanvasWidth,\n sceneCanvasHeight: s.sceneCanvasHeight,\n volumeGate: s.volumeGate === true,\n firmwareUpdateAvailable: s.firmwareUpdateAvailable === true,\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n wifi: {...s.wifi},\n }\n}\n\nexport type GlassesStatusSnapshot = ReturnType<typeof projectStatus>\nexport type KeyboardStatusSnapshot = ReturnType<typeof projectKeyboardStatus>\nexport type GlassesInfoSnapshot = ReturnType<typeof projectInfo>\n\nexport const glasses = {\n // --- connection (bluetooth-sdk passthrough) ---\n /**\n * Reconnect the default wearable. Seeds the phone's current device settings to\n * native first, so they're primed before the connect handshake replays them to\n * the glasses (this used to be a host-side step before `connectDefault`).\n */\n connectDefault: async (): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect_default\")\n throw error\n }\n },\n disconnect: (): Promise<void> => BluetoothSdk.disconnect(),\n forget: (): Promise<void> => BluetoothSdk.forget(),\n /** Connect to a specific (discovered) device. */\n connect: async (device: Device, options?: ConnectOptions): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connect(device, options)\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect\")\n throw error\n }\n },\n /** Connect the built-in simulated glasses (dev/testing). */\n connectSimulated: (): Promise<void> => BluetoothSdk.connectSimulated(),\n /** Set a device as the `connectDefault()` target. */\n setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),\n /**\n * Reconnect to the saved default glasses. Resolves `false` when there's nothing to\n * reconnect to (no default paired) or the connect attempt fails (e.g. BLE powered\n * off), `true` when already connected or after kicking off the connect. Never\n * rejects — the boolean contract holds. The host gates connectivity/permissions\n * (its UI) before calling.\n */\n reconnect: async (): Promise<boolean> => {\n const defaultWearable = useSettingsStore.getState().getSetting(SETTINGS.default_wearable.key)\n if (!defaultWearable) return false\n if (isGlassesConnected(useGlassesStore.getState().connection)) return true\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"reconnect\")\n console.warn(\"engine.glasses.reconnect failed:\", error)\n return false\n }\n return true\n },\n /** True when no glasses has ever been paired NOR selected (the pairing-identity\n * lifecycle is in its `none` state) — e.g. to route a first-run host into the\n * pairing/onboarding flow. A pending selection is not \"first\" — the host should\n * offer finish-pairing (see `engine.pairing.identity()`). */\n isFirstPairing: (): boolean => projectPairingIdentity().kind === \"none\",\n /**\n * True when the SDK has an actual default DEVICE to reconnect to. Distinct from\n * `default_wearable` (a model string that a pending or orphaned selection can\n * hold without any paired device): connectDefault() throws without a device, so\n * hosts should route to the pairing flow when this is false.\n *\n * Hydration-aware: awaits the device-store seed internally, so a cold-start\n * read can't see a false \"absent\" for genuinely-paired users. Rejects if\n * hydration failed — callers guarding destructive or routing decisions must\n * fail open (treat as \"has a device\").\n */\n hasDefaultDevice: (): Promise<boolean> => hasDefaultDevice(),\n\n // --- read-model (projected from the engine-owned glasses store) ---\n status: (): GlassesStatusSnapshot => projectStatus(),\n onStatus: (cb: (status: GlassesStatusSnapshot) => void): (() => void) => {\n // Dedupe: the glasses store updates on many fields; only fire when the\n // projected status snapshot actually changes.\n let last = JSON.stringify(projectStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n info: (): GlassesInfoSnapshot => projectInfo(),\n onInfo: (cb: (info: GlassesInfoSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectInfo())\n return useGlassesStore.subscribe(() => {\n const snap = projectInfo()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n capabilities: () => getModelCapabilities(useGlassesStore.getState().deviceModel as DeviceTypes),\n /** Ask the glasses to report fresh firmware/version info (updates the store). */\n requestVersionInfo: () => BluetoothSdk.requestVersionInfo(),\n\n // --- discrete input events (passthrough listeners) ---\n onButtonPress: (cb: (event: ButtonPressEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"button_press\", cb)\n return () => sub.remove()\n },\n onTouchGesture: (cb: (event: TouchEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"touch_event\", cb)\n return () => sub.remove()\n },\n\n // --- BLE keyboard bridged through G2 Glassly CFW (revision 39+) ---\n keyboard: {\n /** Five-second scan; distinct advertisers, HID ones flagged. Also streams via onAdvert. */\n scan: (): Promise<KeyboardAdvertEvent[]> => BluetoothSdk.g2KeyboardScan(),\n /** Connect, pair and subscribe; the keyboard is remembered and auto-reconnected. */\n connect: async (device: {address: string; addressType: number; name: string}): Promise<void> => {\n await BluetoothSdk.g2KeyboardConnect(device.address, device.addressType, device.name)\n useSettingsStore.getState().setSetting(SETTINGS.keyboard_name.key, device.name)\n },\n reconnect: (): Promise<void> => BluetoothSdk.g2KeyboardReconnect(),\n disconnect: (): Promise<void> => BluetoothSdk.g2KeyboardDisconnect(),\n forget: async (): Promise<void> => {\n await BluetoothSdk.g2KeyboardForget()\n useSettingsStore.getState().setSetting(SETTINGS.keyboard_name.key, \"\")\n },\n passkey: (pin: number): Promise<void> => BluetoothSdk.g2KeyboardPasskey(pin),\n compare: (accept: boolean): Promise<void> => BluetoothSdk.g2KeyboardCompare(accept),\n status: (): KeyboardStatusSnapshot => projectKeyboardStatus(),\n onStatus: (cb: (status: KeyboardStatusSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectKeyboardStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectKeyboardStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n onAdvert: (cb: (advert: KeyboardAdvertEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"keyboard_advert\", cb)\n return () => sub.remove()\n },\n onPairing: (cb: (prompt: KeyboardPairingEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"keyboard_pairing\", cb)\n return () => sub.remove()\n },\n },\n\n // --- controllers (input accessories linked to the phone), by registry id ---\n controllers: {\n /** Paired controllers in pairing order. */\n list: (): PairedController[] => projectControllerList(),\n onList: (cb: (controllers: PairedController[]) => void): (() => void) => {\n let last = JSON.stringify(projectControllerList())\n return useSettingsStore.subscribe(() => {\n const list = projectControllerList()\n const key = JSON.stringify(list)\n if (key === last) return\n last = key\n cb(list)\n })\n },\n /** Live driver statuses by id; a paired controller without an entry is idle. */\n statuses: (): Record<string, ControllerStatus> => projectControllerStatuses(),\n status: (id: string): ControllerStatus | undefined => projectControllerStatuses()[id],\n onStatus: (cb: (statuses: Record<string, ControllerStatus>) => void): (() => void) =>\n useGlassesStore.subscribe(\n (s) => s.controllers,\n () => cb(projectControllerStatuses()),\n ),\n /** Controllers delivering input (linked, route or claim done). */\n connected: (): ControllerStatus[] => readyControllers(projectControllerStatuses()),\n isConnected: (model?: string): boolean =>\n Object.values(useGlassesStore.getState().controllers ?? {}).some(\n (status) => isControllerReady(status) && (model === undefined || status.model === model),\n ),\n connect: (id: string): Promise<void> => BluetoothSdk.connectController(id),\n /** Reconnect every paired controller that is not connected. */\n connectAll: (): Promise<void> => BluetoothSdk.connectControllers(),\n /** Drop an in-flight controller scan or pairing; paired records are untouched. */\n cancelAttempt: (): Promise<void> => BluetoothSdk.cancelControllerAttempt(),\n /** Disconnect; the controller stays paired. */\n disconnect: (id: string): Promise<void> => BluetoothSdk.disconnectController(id),\n forget: async (id: string): Promise<void> => {\n await BluetoothSdk.forgetController(id)\n neuralBandInput.forget(id)\n },\n },\n\n // --- Meta Neural Band (a controller with a claim step and its own features), by controller id ---\n neuralBand: {\n /** Store the Meta account session the one-time band claim uses (account-wide). */\n setMetaSession: (accessToken: string, userId: string): Promise<void> =>\n BluetoothSdk.neuralBandSetMetaSession(accessToken, userId),\n clearMetaSession: (): Promise<void> => BluetoothSdk.neuralBandClearMetaSession(),\n hasMetaSession: (): Promise<boolean> => BluetoothSdk.neuralBandHasMetaSession(),\n /** This band's identity is stored on this phone, so it reconnects without a claim. */\n isClaimed: (id: string): Promise<boolean> => BluetoothSdk.neuralBandIsClaimed(id),\n /** The latest claim progress of any band (with its `controllerId`), kept across screens. */\n claimStatus: (): NeuralBandClaimStatus => ({...neuralBandInput.claimStatus()}),\n resetClaim: (): void => neuralBandInput.resetClaim(),\n onClaimStatus: (cb: (status: NeuralBandClaimStatus) => void): (() => void) => neuralBandInput.onClaimStatus(cb),\n /** The band's handwriting model; writing arrives on the miniapp keyboard stream. */\n setHandwriting: (id: string, enabled: boolean): Promise<void> => BluetoothSdk.neuralBandSetHandwriting(id, enabled),\n handwritingStatus: (id: string): NeuralBandHandwritingStatus => ({...neuralBandInput.handwritingStatus(id)}),\n onHandwritingStatus: (cb: (update: NeuralBandHandwritingUpdate) => void): (() => void) =>\n neuralBandInput.onHandwritingStatus(cb),\n /** The band's air mouse; movement reaches miniapps on the pointer stream. */\n setPointer: (id: string, enabled: boolean): Promise<void> => BluetoothSdk.neuralBandSetPointer(id, enabled),\n setHand: (id: string, hand: \"left\" | \"right\"): Promise<void> => BluetoothSdk.neuralBandSetHand(id, hand),\n },\n\n // --- Apple Watch (iOS): pairs through the Glassly watch app over WatchConnectivity ---\n appleWatch: {\n /** Whether this iPhone has a paired watch and the Glassly watch app is on it. */\n availability: (): Promise<AppleWatchAvailability> => BluetoothSdk.appleWatchAvailability(),\n },\n\n // --- audio (glasses media-volume, own-app playback hint, live mic level) ---\n audio: {\n /** Read the glasses' current media volume. */\n getMediaVolume: () => BluetoothSdk.getGlassesMediaVolume(),\n /** Set the glasses' media volume. */\n setMediaVolume: (level: number) => BluetoothSdk.setGlassesMediaVolume(level),\n /** Tell native whether THIS app is currently playing audio (for ducking). */\n setOwnAppPlaying: (playing: boolean): Promise<void> => BluetoothSdk.setOwnAppAudioPlaying(playing),\n /**\n * Stream the active mic's level in dBFS, one reading per PCM chunk. Holds\n * raw PCM open while subscribed, so the mic runs with no miniapp listening,\n * and holds the G2 volume gate open so the meter shows the ambient level.\n */\n onMicLevel: (cb: (dbfs: number) => void): (() => void) => {\n const owner = `mic_level_${++micLevelSubscriptions}`\n micStateCoordinator.acquireHostPcm(owner)\n const sub = BluetoothSdk.addListener(\"mic_pcm\", (event: {pcm: ArrayBuffer | Uint8Array}) => {\n if (event?.pcm) cb(pcmLevelDbfs(event.pcm))\n })\n return () => {\n sub.remove()\n micStateCoordinator.releaseHostPcm(owner)\n }\n },\n },\n\n // --- sub-facades ---\n wifi: glassesWifi,\n settings: glassesSettings,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * glasses facade — the core `engine.glasses.*` surface: connection actions (over\n * the bluetooth-sdk passthrough), a curated status/info read-model (projected from\n * the engine-owned glasses store), capabilities (from the model table), and the\n * discrete input events. The `wifi` sub-facade is nested here.\n *\n * Read-models project the raw glasses store into the stable shape OEM UIs consume,\n * so the device store's field layout doesn't leak into every screen.\n */\n// Internal btsdk surface — connectSimulated/reconnect/controller live on the full\n// surface, not the public entry. Relative path (the alias doesn't resolve in engine's\n// standalone build); jest moduleNameMapper + tsconfig both resolve it.\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {\n AppleWatchAvailability,\n ButtonPressEvent,\n ConnectOptions,\n ControllerStatus,\n Device,\n KeyboardAdvertEvent,\n KeyboardPairingEvent,\n PairedController,\n TouchEvent,\n} from \"@glassly/bluetooth-sdk\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {hasDefaultDevice} from \"../services/DeviceStoreHydration\"\nimport micStateCoordinator from \"../services/MicStateCoordinator\"\nimport {projectPairingIdentity} from \"../services/PairingIdentity\"\nimport {\n fixtureControllerList,\n fixtureControllerStatuses,\n fixtureInfo,\n fixtureStatus,\n isScreenshotMode,\n} from \"../services/ScreenshotFixture\"\nimport {isGlassesConnected, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {pushAllBluetoothSettings} from \"../services/GlassesSettingsSync\"\nimport {isControllerReady, pairedControllersFrom, readyControllers} from \"../services/controllers\"\nimport neuralBandInput, {\n type NeuralBandClaimStatus,\n type NeuralBandHandwritingStatus,\n type NeuralBandHandwritingUpdate,\n} from \"../services/neuralBand/NeuralBandInput\"\nimport {recordSupportProfileConnectionFailure} from \"../services/SupportProfileSync\"\nimport {getModelCapabilities, type DeviceTypes} from \"../types\"\nimport {pcmLevelDbfs} from \"../utils/audio/pcmLevel\"\nimport {glassesWifi} from \"./glassesWifi\"\nimport {glassesSettings} from \"./glassesSettings\"\n\nlet micLevelSubscriptions = 0\n\n// The screenshot fixture (ScreenshotFixture.ts) dresses these read-models\n// while the identity is the simulated device; it is a no-op outside that build.\nfunction projectStatus() {\n const s = useGlassesStore.getState()\n return fixtureStatus(\n {\n state: s.connection.state,\n fullyBooted: isGlassesReady(s.connection),\n battery: s.batteryLevel,\n charging: s.charging,\n case: {battery: s.caseBatteryLevel, charging: s.caseCharging, open: s.caseOpen, removed: s.caseRemoved},\n signal: s.signalStrength,\n micEnabled: s.micEnabled,\n vadEnabled: s.voiceActivityDetectionEnabled,\n btClassic: s.bluetoothClassicConnected,\n // iOS: glasses may read the phone's ANCS stream (\"Share System Notifications\").\n ancsAuthorized: Boolean(s.ancsAuthorized),\n },\n projectPairingIdentity(),\n )\n}\n\nfunction projectControllerList(): PairedController[] {\n return fixtureControllerList(\n pairedControllersFrom(useSettingsStore.getState().getSetting(SETTINGS.controllers.key)),\n projectPairingIdentity(),\n )\n}\n\n// Copies: snapshots must not hand callers mutable references into the store.\nfunction projectControllerStatuses(): Record<string, ControllerStatus> {\n const statuses: Record<string, ControllerStatus> = {}\n for (const [id, status] of Object.entries(useGlassesStore.getState().controllers ?? {})) {\n statuses[id] = {...status}\n }\n return fixtureControllerStatuses(statuses, projectPairingIdentity())\n}\n\n// Dedupe on the projected snapshot: the store updates on many fields; fire\n// only when the projection changes. The fixture's read-models also follow the\n// identity (a settings key), so that build watches the settings store too.\nfunction subscribeProjection<T>(project: () => T, cb: (snapshot: T) => void): () => void {\n let last = JSON.stringify(project())\n const notify = () => {\n const snap = project()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n }\n const unsubscribeGlasses = useGlassesStore.subscribe(notify)\n const unsubscribeSettings = isScreenshotMode() ? useSettingsStore.subscribe(notify) : () => {}\n return () => {\n unsubscribeGlasses()\n unsubscribeSettings()\n }\n}\n\nfunction projectKeyboardStatus() {\n const s = useGlassesStore.getState()\n return {\n /** Connected glasses run a firmware that can host a keyboard (G2 Glassly CFW 39+). */\n available: s.keyboardBridge === true,\n connected: s.keyboardConnected === true,\n name: s.keyboardName ?? \"\",\n /** A keyboard is remembered and reconnects automatically. */\n saved: s.keyboardSaved === true,\n state: s.keyboardState ?? \"idle\",\n error: s.keyboardError ?? \"\",\n }\n}\n\nfunction projectInfo() {\n const s = useGlassesStore.getState()\n return fixtureInfo(projectRawInfo(s), projectPairingIdentity())\n}\n\nfunction projectRawInfo(s: ReturnType<typeof useGlassesStore.getState>) {\n return {\n model: s.deviceModel,\n style: s.style,\n color: s.color,\n bluetoothName: s.bluetoothName,\n firmwareVersion: s.firmwareVersion,\n mtkFirmware: s.mtkFirmwareVersion,\n besFirmware: s.besFirmwareVersion,\n appVersion: s.appVersion,\n androidVersion: s.androidVersion,\n serialNumber: s.serialNumber,\n buildNumber: s.buildNumber,\n btMac: s.bluetoothMacAddress,\n wifiMac: s.wifiMacAddress,\n leftMac: s.leftMacAddress,\n rightMac: s.rightMacAddress,\n sceneCanvasWidth: s.sceneCanvasWidth,\n sceneCanvasHeight: s.sceneCanvasHeight,\n volumeGate: s.volumeGate === true,\n firmwareUpdateAvailable: s.firmwareUpdateAvailable === true,\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n wifi: {...s.wifi},\n }\n}\n\nexport type GlassesStatusSnapshot = ReturnType<typeof projectStatus>\nexport type KeyboardStatusSnapshot = ReturnType<typeof projectKeyboardStatus>\nexport type GlassesInfoSnapshot = ReturnType<typeof projectInfo>\n\nexport const glasses = {\n // --- connection (bluetooth-sdk passthrough) ---\n /**\n * Reconnect the default wearable. Seeds the phone's current device settings to\n * native first, so they're primed before the connect handshake replays them to\n * the glasses (this used to be a host-side step before `connectDefault`).\n */\n connectDefault: async (): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect_default\")\n throw error\n }\n },\n disconnect: (): Promise<void> => BluetoothSdk.disconnect(),\n forget: (): Promise<void> => BluetoothSdk.forget(),\n /** Connect to a specific (discovered) device. */\n connect: async (device: Device, options?: ConnectOptions): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connect(device, options)\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect\")\n throw error\n }\n },\n /** Connect the built-in simulated glasses (dev/testing). */\n connectSimulated: (): Promise<void> => BluetoothSdk.connectSimulated(),\n /** Set a device as the `connectDefault()` target. */\n setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),\n /**\n * Reconnect to the saved default glasses. Resolves `false` when there's nothing to\n * reconnect to (no default paired) or the connect attempt fails (e.g. BLE powered\n * off), `true` when already connected or after kicking off the connect. Never\n * rejects — the boolean contract holds. The host gates connectivity/permissions\n * (its UI) before calling.\n */\n reconnect: async (): Promise<boolean> => {\n const defaultWearable = useSettingsStore.getState().getSetting(SETTINGS.default_wearable.key)\n if (!defaultWearable) return false\n if (isGlassesConnected(useGlassesStore.getState().connection)) return true\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"reconnect\")\n console.warn(\"engine.glasses.reconnect failed:\", error)\n return false\n }\n return true\n },\n /** True when no glasses has ever been paired NOR selected (the pairing-identity\n * lifecycle is in its `none` state) — e.g. to route a first-run host into the\n * pairing/onboarding flow. A pending selection is not \"first\" — the host should\n * offer finish-pairing (see `engine.pairing.identity()`). */\n isFirstPairing: (): boolean => projectPairingIdentity().kind === \"none\",\n /**\n * True when the SDK has an actual default DEVICE to reconnect to. Distinct from\n * `default_wearable` (a model string that a pending or orphaned selection can\n * hold without any paired device): connectDefault() throws without a device, so\n * hosts should route to the pairing flow when this is false.\n *\n * Hydration-aware: awaits the device-store seed internally, so a cold-start\n * read can't see a false \"absent\" for genuinely-paired users. Rejects if\n * hydration failed — callers guarding destructive or routing decisions must\n * fail open (treat as \"has a device\").\n */\n hasDefaultDevice: (): Promise<boolean> => hasDefaultDevice(),\n\n // --- read-model (projected from the engine-owned glasses store) ---\n status: (): GlassesStatusSnapshot => projectStatus(),\n onStatus: (cb: (status: GlassesStatusSnapshot) => void): (() => void) => subscribeProjection(projectStatus, cb),\n info: (): GlassesInfoSnapshot => projectInfo(),\n onInfo: (cb: (info: GlassesInfoSnapshot) => void): (() => void) => subscribeProjection(projectInfo, cb),\n capabilities: () => getModelCapabilities(useGlassesStore.getState().deviceModel as DeviceTypes),\n /** Ask the glasses to report fresh firmware/version info (updates the store). */\n requestVersionInfo: () => BluetoothSdk.requestVersionInfo(),\n\n // --- discrete input events (passthrough listeners) ---\n onButtonPress: (cb: (event: ButtonPressEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"button_press\", cb)\n return () => sub.remove()\n },\n onTouchGesture: (cb: (event: TouchEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"touch_event\", cb)\n return () => sub.remove()\n },\n\n // --- BLE keyboard bridged through G2 Glassly CFW (revision 39+) ---\n keyboard: {\n /** Five-second scan; distinct advertisers, HID ones flagged. Also streams via onAdvert. */\n scan: (): Promise<KeyboardAdvertEvent[]> => BluetoothSdk.g2KeyboardScan(),\n /** Connect, pair and subscribe; the keyboard is remembered and auto-reconnected. */\n connect: async (device: {address: string; addressType: number; name: string}): Promise<void> => {\n await BluetoothSdk.g2KeyboardConnect(device.address, device.addressType, device.name)\n useSettingsStore.getState().setSetting(SETTINGS.keyboard_name.key, device.name)\n },\n reconnect: (): Promise<void> => BluetoothSdk.g2KeyboardReconnect(),\n disconnect: (): Promise<void> => BluetoothSdk.g2KeyboardDisconnect(),\n forget: async (): Promise<void> => {\n await BluetoothSdk.g2KeyboardForget()\n useSettingsStore.getState().setSetting(SETTINGS.keyboard_name.key, \"\")\n },\n passkey: (pin: number): Promise<void> => BluetoothSdk.g2KeyboardPasskey(pin),\n compare: (accept: boolean): Promise<void> => BluetoothSdk.g2KeyboardCompare(accept),\n status: (): KeyboardStatusSnapshot => projectKeyboardStatus(),\n onStatus: (cb: (status: KeyboardStatusSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectKeyboardStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectKeyboardStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n onAdvert: (cb: (advert: KeyboardAdvertEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"keyboard_advert\", cb)\n return () => sub.remove()\n },\n onPairing: (cb: (prompt: KeyboardPairingEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"keyboard_pairing\", cb)\n return () => sub.remove()\n },\n },\n\n // --- controllers (input accessories linked to the phone), by registry id ---\n controllers: {\n /** Paired controllers in pairing order. */\n list: (): PairedController[] => projectControllerList(),\n onList: (cb: (controllers: PairedController[]) => void): (() => void) => {\n let last = JSON.stringify(projectControllerList())\n return useSettingsStore.subscribe(() => {\n const list = projectControllerList()\n const key = JSON.stringify(list)\n if (key === last) return\n last = key\n cb(list)\n })\n },\n /** Live driver statuses by id; a paired controller without an entry is idle. */\n statuses: (): Record<string, ControllerStatus> => projectControllerStatuses(),\n status: (id: string): ControllerStatus | undefined => projectControllerStatuses()[id],\n onStatus: (cb: (statuses: Record<string, ControllerStatus>) => void): (() => void) =>\n subscribeProjection(projectControllerStatuses, cb),\n /** Controllers delivering input (linked, route or claim done). */\n connected: (): ControllerStatus[] => readyControllers(projectControllerStatuses()),\n isConnected: (model?: string): boolean =>\n Object.values(projectControllerStatuses()).some(\n (status) => isControllerReady(status) && (model === undefined || status.model === model),\n ),\n connect: (id: string): Promise<void> => BluetoothSdk.connectController(id),\n /** Reconnect every paired controller that is not connected. */\n connectAll: (): Promise<void> => BluetoothSdk.connectControllers(),\n /** Drop an in-flight controller scan or pairing; paired records are untouched. */\n cancelAttempt: (): Promise<void> => BluetoothSdk.cancelControllerAttempt(),\n /** Disconnect; the controller stays paired. */\n disconnect: (id: string): Promise<void> => BluetoothSdk.disconnectController(id),\n forget: async (id: string): Promise<void> => {\n await BluetoothSdk.forgetController(id)\n neuralBandInput.forget(id)\n },\n },\n\n // --- Meta Neural Band (a controller with a claim step and its own features), by controller id ---\n neuralBand: {\n /** Store the Meta account session the one-time band claim uses (account-wide). */\n setMetaSession: (accessToken: string, userId: string): Promise<void> =>\n BluetoothSdk.neuralBandSetMetaSession(accessToken, userId),\n clearMetaSession: (): Promise<void> => BluetoothSdk.neuralBandClearMetaSession(),\n hasMetaSession: (): Promise<boolean> => BluetoothSdk.neuralBandHasMetaSession(),\n /** This band's identity is stored on this phone, so it reconnects without a claim. */\n isClaimed: (id: string): Promise<boolean> => BluetoothSdk.neuralBandIsClaimed(id),\n /** The latest claim progress of any band (with its `controllerId`), kept across screens. */\n claimStatus: (): NeuralBandClaimStatus => ({...neuralBandInput.claimStatus()}),\n resetClaim: (): void => neuralBandInput.resetClaim(),\n onClaimStatus: (cb: (status: NeuralBandClaimStatus) => void): (() => void) => neuralBandInput.onClaimStatus(cb),\n /** The band's handwriting model; writing arrives on the miniapp keyboard stream. */\n setHandwriting: (id: string, enabled: boolean): Promise<void> => BluetoothSdk.neuralBandSetHandwriting(id, enabled),\n handwritingStatus: (id: string): NeuralBandHandwritingStatus => ({...neuralBandInput.handwritingStatus(id)}),\n onHandwritingStatus: (cb: (update: NeuralBandHandwritingUpdate) => void): (() => void) =>\n neuralBandInput.onHandwritingStatus(cb),\n /** The band's air mouse; movement reaches miniapps on the pointer stream. */\n setPointer: (id: string, enabled: boolean): Promise<void> => BluetoothSdk.neuralBandSetPointer(id, enabled),\n setHand: (id: string, hand: \"left\" | \"right\"): Promise<void> => BluetoothSdk.neuralBandSetHand(id, hand),\n },\n\n // --- Apple Watch (iOS): pairs through the Glassly watch app over WatchConnectivity ---\n appleWatch: {\n /** Whether this iPhone has a paired watch and the Glassly watch app is on it. */\n availability: (): Promise<AppleWatchAvailability> => BluetoothSdk.appleWatchAvailability(),\n },\n\n // --- audio (glasses media-volume, own-app playback hint, live mic level) ---\n audio: {\n /** Read the glasses' current media volume. */\n getMediaVolume: () => BluetoothSdk.getGlassesMediaVolume(),\n /** Set the glasses' media volume. */\n setMediaVolume: (level: number) => BluetoothSdk.setGlassesMediaVolume(level),\n /** Tell native whether THIS app is currently playing audio (for ducking). */\n setOwnAppPlaying: (playing: boolean): Promise<void> => BluetoothSdk.setOwnAppAudioPlaying(playing),\n /**\n * Stream the active mic's level in dBFS, one reading per PCM chunk. Holds\n * raw PCM open while subscribed, so the mic runs with no miniapp listening,\n * and holds the G2 volume gate open so the meter shows the ambient level.\n */\n onMicLevel: (cb: (dbfs: number) => void): (() => void) => {\n const owner = `mic_level_${++micLevelSubscriptions}`\n micStateCoordinator.acquireHostPcm(owner)\n const sub = BluetoothSdk.addListener(\"mic_pcm\", (event: {pcm: ArrayBuffer | Uint8Array}) => {\n if (event?.pcm) cb(pcmLevelDbfs(event.pcm))\n })\n return () => {\n sub.remove()\n micStateCoordinator.releaseHostPcm(owner)\n }\n },\n },\n\n // --- sub-facades ---\n wifi: glassesWifi,\n settings: glassesSettings,\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/facades/pairing.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAC,MAAM,wBAAwB,CAAA;AAKvH,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/facades/pairing.ts"],"names":[],"mappings":"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"}
|
package/build/facades/pairing.js
CHANGED
|
@@ -15,6 +15,7 @@ import { useGlassesStore } from "../stores/glasses";
|
|
|
15
15
|
import { useSettingsStore } from "../stores/settings";
|
|
16
16
|
import { hasDefaultDevice } from "../services/DeviceStoreHydration";
|
|
17
17
|
import { markPendingSelection, projectPairingIdentity, subscribePairingIdentity, } from "../services/PairingIdentity";
|
|
18
|
+
import { fixtureIdentity } from "../services/ScreenshotFixture";
|
|
18
19
|
import { isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady } from "../services/GlassesReadiness";
|
|
19
20
|
import { logAutomaticReportSubmissionStatus, toAutomaticReportSubmissionStatus, } from "../services/AutomaticReportResult";
|
|
20
21
|
import { pushAllBluetoothSettings } from "../services/GlassesSettingsSync";
|
|
@@ -219,10 +220,10 @@ export const pairing = {
|
|
|
219
220
|
// --- pairing-identity lifecycle (the PairingIdentity read-model + the JS-owned
|
|
220
221
|
// identity writes; promotion to `paired` only ever happens natively) ---
|
|
221
222
|
/** The identity lifecycle snapshot: none | pending (chosen, never paired) | paired. */
|
|
222
|
-
identity: () => projectPairingIdentity(),
|
|
223
|
+
identity: () => fixtureIdentity(projectPairingIdentity()),
|
|
223
224
|
/** Subscribe to identity changes; fires only when the projected snapshot changes.
|
|
224
225
|
* Returns an unsubscribe. */
|
|
225
|
-
onIdentity: (cb) => subscribePairingIdentity(cb),
|
|
226
|
+
onIdentity: (cb) => subscribePairingIdentity((identity) => cb(fixtureIdentity(identity))),
|
|
226
227
|
/** Mark the chosen model as the pending selection (the scan-entry write); the
|
|
227
228
|
* host renders it as a finish-pairing affordance until pairing succeeds. */
|
|
228
229
|
markPendingSelection: (model) => markPendingSelection(model),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/facades/pairing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * pairing facade — `engine.pairing`: scan for nearby glasses + pair. Scan state\n * (searching + results) comes from the engine core store; scan/connect go over the\n * bluetooth-sdk; pair-failure / not-ready are the bluetooth-sdk events.\n *\n * (Connect-the-already-paired-default is `engine.glasses.connectDefault()`; this\n * facade is the first-time discovery + pair flow.)\n */\n// Internal btsdk surface — updateBluetoothSettings (the Bluetooth Classic\n// target hint) lives on the full surface, not the public entry (same reason\n// the glasses facade imports internal).\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {ConnectOptions, Device, DeviceModel, PairFailureEvent, GlassesNotReadyEvent} from \"@glassly/bluetooth-sdk\"\nimport {useCoreStore} from \"../stores/core\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {hasDefaultDevice} from \"../services/DeviceStoreHydration\"\nimport {\n markPendingSelection,\n projectPairingIdentity,\n subscribePairingIdentity,\n type IdentitySnapshot,\n} from \"../services/PairingIdentity\"\nimport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {\n logAutomaticReportSubmissionStatus,\n toAutomaticReportSubmissionStatus,\n type AutomaticReportSubmissionStatus,\n} from \"../services/AutomaticReportResult\"\nimport {pushAllBluetoothSettings} from \"../services/GlassesSettingsSync\"\nimport {isControllerModel, isControllerReady} from \"../services/controllers\"\nimport {submitAutomaticReport} from \"./reports\"\n\nimport {BgTimer} from \"../utils/timers\"\n\nexport type {IdentitySnapshot} from \"../services/PairingIdentity\"\n\nexport interface PairingReadyWaitOptions {\n deviceModel?: string\n deviceName?: string\n timeoutMs?: number\n route?: string\n signal?: AbortSignal\n}\n\nconst DEFAULT_PAIRING_BOOT_TIMEOUT_MS = 35_000\nconst DEFAULT_BT_CLASSIC_TIMEOUT_MS = 1_000\nconst DEFAULT_PAIRING_ROUTE = \"/pairing/loading\"\nconst LOG_TAG = \"PairingTimeoutReport\"\n\nfunction projectReadiness() {\n const s = useGlassesStore.getState()\n return {\n state: s.connection.state,\n connected: s.connection.state === \"connected\",\n fullyBooted: isGlassesReady(s.connection),\n bluetoothClassicConnected: s.bluetoothClassicConnected,\n nativeLinkBusy: isGlassesLinkLayerBusy(s.connection),\n }\n}\n\nfunction projectTargetReady(options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">): boolean {\n const {deviceModel, deviceName} = options\n const glasses = useGlassesStore.getState()\n\n // A controller target is ready once some driver of that model delivers input;\n // the scan's device name is the R1's ring id or the band's Bluetooth name.\n if (isControllerModel(deviceModel)) {\n return Object.values(glasses.controllers ?? {}).some(\n (status) =>\n status.model === deviceModel &&\n isControllerReady(status) &&\n (!deviceName || status.name === deviceName || status.id === deviceName),\n )\n }\n\n if (!isGlassesReady(glasses.connection)) return false\n const identity = projectPairingIdentity()\n if (!deviceModel && !deviceName) return true\n return (\n identity.kind === \"paired\" &&\n (!deviceModel || identity.model === deviceModel) &&\n (!deviceName || identity.name === deviceName)\n )\n}\n\nfunction subscribeTargetReady(\n options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">,\n cb: (ready: boolean) => void,\n): () => void {\n let last = projectTargetReady(options)\n const notify = () => {\n const ready = projectTargetReady(options)\n if (ready === last) return\n last = ready\n cb(ready)\n }\n const unsubscribeGlasses = useGlassesStore.subscribe(notify)\n const unsubscribeSettings = useSettingsStore.subscribe(notify)\n return () => {\n unsubscribeGlasses()\n unsubscribeSettings()\n }\n}\n\nasync function waitForTargetReady(options: PairingReadyWaitOptions, timeoutMs: number): Promise<boolean> {\n return new Promise((resolve) => {\n if (options.signal?.aborted) {\n resolve(false)\n return\n }\n if (projectTargetReady(options)) {\n resolve(true)\n return\n }\n\n let settled = false\n let timeout: number | null = null\n let unsubscribe: (() => void) | null = null\n let onAbort: (() => void) | null = null\n const finish = (ready: boolean) => {\n if (settled) return\n settled = true\n if (timeout) BgTimer.clearTimeout(timeout)\n if (unsubscribe) unsubscribe()\n if (onAbort && options.signal) options.signal.removeEventListener(\"abort\", onAbort)\n resolve(ready)\n }\n\n unsubscribe = subscribeTargetReady(options, (ready) => {\n if (ready) finish(true)\n })\n if (options.signal) {\n onAbort = () => finish(false)\n options.signal.addEventListener(\"abort\", onAbort)\n }\n timeout = BgTimer.setTimeout(() => finish(projectTargetReady(options)), timeoutMs)\n })\n}\n\nexport type PairingReadinessSnapshot = ReturnType<typeof projectReadiness>\n\nexport async function submitPairingBootTimeoutReport(params: {\n deviceModel?: string\n deviceName?: string\n showGlassesBooting: boolean\n elapsedMs: number\n route?: string\n}): Promise<AutomaticReportSubmissionStatus> {\n const {deviceModel, deviceName, showGlassesBooting, elapsedMs, route = DEFAULT_PAIRING_ROUTE} = params\n const result = await submitAutomaticReport({\n kind: \"automatic\",\n trigger: {\n type: \"automatic\",\n source: \"pairing_loading\",\n reason: \"glasses_connect_timeout\",\n },\n report: {\n expectedBehavior: `Glasses should connect successfully within ${Math.round(elapsedMs / 1000)} seconds.`,\n actualBehavior: JSON.stringify(\n {\n deviceModel,\n deviceName,\n showGlassesBooting,\n elapsedMs,\n route,\n },\n null,\n 2,\n ),\n systemPriority: \"medium\",\n },\n throttleKey: `pairing_timeout|${deviceModel || \"unknown\"}|${deviceName || \"unknown\"}`,\n })\n\n const status = toAutomaticReportSubmissionStatus(result)\n logAutomaticReportSubmissionStatus(LOG_TAG, status, deviceModel, deviceName)\n return status\n}\n\nasync function waitForReadyDuringPairing(options: PairingReadyWaitOptions = {}): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_PAIRING_BOOT_TIMEOUT_MS\n let showGlassesBooting = false\n const notReadySub = BluetoothSdk.addListener(\"glasses_not_ready\", () => {\n showGlassesBooting = true\n })\n\n try {\n const ready = await waitForTargetReady(options, timeoutMs)\n\n if (!ready && !options.signal?.aborted) {\n void submitPairingBootTimeoutReport({\n deviceModel: options.deviceModel,\n deviceName: options.deviceName,\n showGlassesBooting,\n elapsedMs: timeoutMs,\n route: options.route,\n }).catch((error) => {\n console.error(`[${LOG_TAG}] Unexpected error:`, error)\n })\n }\n\n return ready\n } finally {\n notReadySub.remove()\n }\n}\n\nasync function waitForBluetoothClassic(\n options: Pick<PairingReadyWaitOptions, \"timeoutMs\" | \"signal\"> = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_BT_CLASSIC_TIMEOUT_MS\n return new Promise((resolve) => {\n if (options.signal?.aborted) {\n resolve(false)\n return\n }\n\n const initial = useGlassesStore.getState().bluetoothClassicConnected\n if (initial) {\n resolve(true)\n return\n }\n\n let settled = false\n let timeout: number | null = null\n let unsubscribe: (() => void) | null = null\n let onAbort: (() => void) | null = null\n\n const finish = (value: boolean) => {\n if (settled) return\n settled = true\n if (timeout) BgTimer.clearTimeout(timeout)\n if (unsubscribe) unsubscribe()\n if (onAbort && options.signal) options.signal.removeEventListener(\"abort\", onAbort)\n resolve(value)\n }\n\n if (options.signal) {\n onAbort = () => finish(false)\n options.signal.addEventListener(\"abort\", onAbort)\n }\n\n unsubscribe = useGlassesStore.subscribe(\n (s) => s.bluetoothClassicConnected,\n (connected) => {\n if (connected) finish(true)\n },\n )\n\n timeout = BgTimer.setTimeout(() => {\n finish(useGlassesStore.getState().bluetoothClassicConnected)\n }, timeoutMs)\n })\n}\n\nexport const pairing = {\n readiness: (): PairingReadinessSnapshot => projectReadiness(),\n onReadiness: (cb: (readiness: PairingReadinessSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectReadiness())\n return useGlassesStore.subscribe(() => {\n const snap = projectReadiness()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n /** Whether the exact wearable or controller selected on the scan screen is ready. */\n targetReady: (options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">): boolean =>\n projectTargetReady(options),\n /** Subscribe to readiness for the exact selected target, excluding stale devices. */\n onTargetReady: (\n options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">,\n cb: (ready: boolean) => void,\n ): (() => void) => subscribeTargetReady(options, cb),\n\n // --- pairing-identity lifecycle (the PairingIdentity read-model + the JS-owned\n // identity writes; promotion to `paired` only ever happens natively) ---\n /** The identity lifecycle snapshot: none | pending (chosen, never paired) | paired. */\n identity: (): IdentitySnapshot => projectPairingIdentity(),\n /** Subscribe to identity changes; fires only when the projected snapshot changes.\n * Returns an unsubscribe. */\n onIdentity: (cb: (identity: IdentitySnapshot) => void): (() => void) => subscribePairingIdentity(cb),\n /** Mark the chosen model as the pending selection (the scan-entry write); the\n * host renders it as a finish-pairing affordance until pairing succeeds. */\n markPendingSelection: (model: string) => markPendingSelection(model),\n\n /** Start scanning for nearby glasses or controllers of `model`. Results land on `searchResults()`/`onFound()`. */\n scan: (model: DeviceModel): Promise<void> => BluetoothSdk.startScan(model),\n /** Whether a scan is currently in progress. */\n scanning: (): boolean => useCoreStore.getState().searching,\n /** Subscribe to scan-in-progress changes; fires only when it changes. Returns an unsubscribe. */\n onScanning: (cb: (scanning: boolean) => void): (() => void) => useCoreStore.subscribe((s) => s.searching, cb),\n /** Whether the phone has another Bluetooth (audio) device connected — the BT Classic pairing hint. */\n otherBtConnected: (): boolean => useCoreStore.getState().otherBtConnected,\n /** Subscribe to other-Bluetooth-device-connected changes; fires only when it changes. Returns an unsubscribe. */\n onOtherBtConnected: (cb: (connected: boolean) => void): (() => void) =>\n useCoreStore.subscribe((s) => s.otherBtConnected, cb),\n /** The current scan results (snapshot). */\n // Copy the array AND each entry: neither the list nor the Device objects may\n // leak as mutable references into the store.\n searchResults: () => useCoreStore.getState().searchResults.map((result) => ({...result})),\n /** Subscribe to scan-result changes; fires only when they change. Returns an unsubscribe. */\n onFound: (cb: (results: Device[]) => void): (() => void) => {\n let last = JSON.stringify(useCoreStore.getState().searchResults)\n return useCoreStore.subscribe(() => {\n const results = useCoreStore.getState().searchResults\n const key = JSON.stringify(results)\n if (key === last) return\n last = key\n // Copy like searchResults(): listeners must not mutate shared scan state.\n cb(results.map((result) => ({...result})))\n })\n },\n\n /**\n * Pair with (connect to) a discovered device. Two-phase identity: the connect\n * attempt only marks the device as pending (`saveAsDefault: false` — no eager\n * default-device write); the native layer promotes it to the default wearable\n * when pairing actually succeeds (handleDeviceReady), so an abandoned or\n * failed attempt can't leave a default identity with no paired device behind.\n */\n pair: async (device: Device, options?: ConnectOptions): Promise<void> => {\n await pushAllBluetoothSettings()\n return BluetoothSdk.connect(device, {...options, saveAsDefault: false})\n },\n /** Set a device as the default for subsequent `glasses.connectDefault()`. */\n setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),\n /**\n * Prime the native Bluetooth Classic audio watcher with the picked device.\n * The iOS Mentra Live flow pairs Classic audio BEFORE any BLE connect\n * exists, and native detects the pairing by matching the connected audio\n * route against its device_name — which two-phase identity no longer sets\n * at selection time. This is native-only routing state (device_name has no\n * native→JS echo): the phone's persisted identity is untouched, and\n * promotion still happens only at pairing success.\n */\n setBluetoothClassicTarget: (device: Device): Promise<void> =>\n BluetoothSdk.updateBluetoothSettings({device_name: device.name}),\n /**\n * Abandon an in-flight pairing attempt (back-out, failure retry, conflict\n * retry) without destroying an existing pairing. The decision comes from the\n * LIVE hydrated default-device read — never from flow-entry state, because a\n * pairing can PROMOTE while the flow is open (the glasses finish pairing\n * even as the user backs out of the UI), and an entry snapshot would forget\n * that brand-new pairing:\n * - Default device exists and glasses are connected (nothing in flight — an\n * attempt drops the link first): stop the scan, touch nothing else.\n * - Default device exists with an attempt in flight: cancel it, then re-seed\n * the native identity from the phone's persisted settings — the attempt's\n * connect-by-name overwrote the native device_name, so a later\n * connectDefault() would otherwise target the abandoned device.\n * - No default device (genuinely unpaired attempt): also forget, clearing\n * the partial native pairing state.\n * The read fails OPEN to \"preserve\" — a transient failure must never wipe a\n * real pairing. The `pending_wearable` marker is deliberately left alone —\n * the host renders it as a finish-pairing affordance.\n */\n abandonAttempt: async (): Promise<void> => {\n const nativeHasDefault = await hasDefaultDevice().catch(() => true)\n if (nativeHasDefault && isGlassesConnected(useGlassesStore.getState().connection)) {\n // Still connected means no connect attempt is in flight (an attempt drops\n // the existing link first): the user browsed the scan and backed out.\n // Stop the scan and leave the live pairing untouched.\n console.log(\"PairingIdentity: abandonAttempt — pairing intact and connected; stopping scan only\")\n await BluetoothSdk.stopScan()\n return\n }\n if (projectPairingIdentity().kind === \"paired\") {\n // The persisted settings describe a COMPLETE pairing: restore it to\n // native (the attempt's connect-by-name overwrote the native\n // device_name; and if native somehow lost its default entirely, this\n // repairs the divergence instead of forgetting a real pairing).\n console.log(\"PairingIdentity: abandonAttempt — preserving pairing; attempt cancelled, native identity re-seeded\")\n await BluetoothSdk.disconnect()\n await pushAllBluetoothSettings()\n return\n }\n if (nativeHasDefault) {\n // Mid-relay: native promoted and its echoes are still landing — the\n // incomplete JS snapshot must not be pushed over the fresher native\n // identity (the on-connect replay's race). Native holds the truth.\n console.log(\"PairingIdentity: abandonAttempt — preserving pairing; JS identity mid-relay, native kept as-is\")\n await BluetoothSdk.disconnect()\n return\n }\n // Partial attempt: forget owns teardown. Do NOT disconnect() first — that\n // nulls the MentraLive SGC and used to skip Classic removeBond, leaving\n // IBRT ACL up so glasses never re-advertise for the next scan.\n console.log(\"PairingIdentity: abandonAttempt — no pairing on either layer; forgetting the partial attempt\")\n await BluetoothSdk.forget()\n },\n\n /**\n * Abandon an in-flight controller pairing (back-out, failure retry). Only the\n * attempt's driver is dropped; paired controllers and the glasses are untouched.\n */\n abandonControllerAttempt: (): Promise<void> => BluetoothSdk.cancelControllerAttempt(),\n\n /** Subscribe to pairing failures; returns an unsubscribe. */\n onPairFailure: (cb: (event: PairFailureEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"pair_failure\", cb)\n return () => sub.remove()\n },\n /** Subscribe to glasses-not-ready events during pairing; returns an unsubscribe. */\n onGlassesNotReady: (cb: (event: GlassesNotReadyEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"glasses_not_ready\", cb)\n return () => sub.remove()\n },\n /** Wait for paired glasses to finish booting; engine files the timeout diagnostic. */\n waitForReady: waitForReadyDuringPairing,\n /** Wait briefly for Bluetooth Classic to come up after BLE pairing. */\n waitForBluetoothClassic,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/facades/pairing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,0EAA0E;AAC1E,4EAA4E;AAC5E,wCAAwC;AACxC,OAAO,YAAY,MAAM,iCAAiC,CAAA;AAE1D,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kCAAkC,CAAA;AACjE,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,GAEzB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAA;AAC7D,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAC,MAAM,8BAA8B,CAAA;AACvG,OAAO,EACL,kCAAkC,EAClC,iCAAiC,GAElC,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAC,wBAAwB,EAAC,MAAM,iCAAiC,CAAA;AACxE,OAAO,EAAC,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,yBAAyB,CAAA;AAC5E,OAAO,EAAC,qBAAqB,EAAC,MAAM,WAAW,CAAA;AAE/C,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AAYvC,MAAM,+BAA+B,GAAG,MAAM,CAAA;AAC9C,MAAM,6BAA6B,GAAG,KAAK,CAAA;AAC3C,MAAM,qBAAqB,GAAG,kBAAkB,CAAA;AAChD,MAAM,OAAO,GAAG,sBAAsB,CAAA;AAEtC,SAAS,gBAAgB;IACvB,MAAM,CAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IACpC,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC,KAAK;QACzB,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC,KAAK,KAAK,WAAW;QAC7C,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC;QACzC,yBAAyB,EAAE,CAAC,CAAC,yBAAyB;QACtD,cAAc,EAAE,sBAAsB,CAAC,CAAC,CAAC,UAAU,CAAC;KACrD,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAoE;IAC9F,MAAM,EAAC,WAAW,EAAE,UAAU,EAAC,GAAG,OAAO,CAAA;IACzC,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;IAE1C,8EAA8E;IAC9E,2EAA2E;IAC3E,IAAI,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAClD,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,KAAK,KAAK,WAAW;YAC5B,iBAAiB,CAAC,MAAM,CAAC;YACzB,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,MAAM,CAAC,EAAE,KAAK,UAAU,CAAC,CAC1E,CAAA;IACH,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAA;IACrD,MAAM,QAAQ,GAAG,sBAAsB,EAAE,CAAA;IACzC,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAA;IAC5C,OAAO,CACL,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAC1B,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC,KAAK,KAAK,WAAW,CAAC;QAChD,CAAC,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC,CAC9C,CAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,OAAoE,EACpE,EAA4B;IAE5B,IAAI,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;QACzC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAM;QAC1B,IAAI,GAAG,KAAK,CAAA;QACZ,EAAE,CAAC,KAAK,CAAC,CAAA;IACX,CAAC,CAAA;IACD,MAAM,kBAAkB,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5D,MAAM,mBAAmB,GAAG,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC9D,OAAO,GAAG,EAAE;QACV,kBAAkB,EAAE,CAAA;QACpB,mBAAmB,EAAE,CAAA;IACvB,CAAC,CAAA;AACH,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,OAAgC,EAAE,SAAiB;IACnF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,CAAA;YACd,OAAM;QACR,CAAC;QACD,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,CAAA;YACb,OAAM;QACR,CAAC;QAED,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,OAAO,GAAkB,IAAI,CAAA;QACjC,IAAI,WAAW,GAAwB,IAAI,CAAA;QAC3C,IAAI,OAAO,GAAwB,IAAI,CAAA;QACvC,MAAM,MAAM,GAAG,CAAC,KAAc,EAAE,EAAE;YAChC,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,IAAI,OAAO;gBAAE,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;YAC1C,IAAI,WAAW;gBAAE,WAAW,EAAE,CAAA;YAC9B,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM;gBAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YACnF,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC,CAAA;QAED,WAAW,GAAG,oBAAoB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACpD,IAAI,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;QACF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC7B,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACnD,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;IACpF,CAAC,CAAC,CAAA;AACJ,CAAC;AAID,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,MAMpD;IACC,MAAM,EAAC,WAAW,EAAE,UAAU,EAAE,kBAAkB,EAAE,SAAS,EAAE,KAAK,GAAG,qBAAqB,EAAC,GAAG,MAAM,CAAA;IACtG,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC;QACzC,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE;YACP,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,iBAAiB;YACzB,MAAM,EAAE,yBAAyB;SAClC;QACD,MAAM,EAAE;YACN,gBAAgB,EAAE,8CAA8C,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW;YACvG,cAAc,EAAE,IAAI,CAAC,SAAS,CAC5B;gBACE,WAAW;gBACX,UAAU;gBACV,kBAAkB;gBAClB,SAAS;gBACT,KAAK;aACN,EACD,IAAI,EACJ,CAAC,CACF;YACD,cAAc,EAAE,QAAQ;SACzB;QACD,WAAW,EAAE,mBAAmB,WAAW,IAAI,SAAS,IAAI,UAAU,IAAI,SAAS,EAAE;KACtF,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,iCAAiC,CAAC,MAAM,CAAC,CAAA;IACxD,kCAAkC,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAA;IAC5E,OAAO,MAAM,CAAA;AACf,CAAC;AAED,KAAK,UAAU,yBAAyB,CAAC,UAAmC,EAAE;IAC5E,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,+BAA+B,CAAA;IACtE,IAAI,kBAAkB,GAAG,KAAK,CAAA;IAC9B,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACrE,kBAAkB,GAAG,IAAI,CAAA;IAC3B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAE1D,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YACvC,KAAK,8BAA8B,CAAC;gBAClC,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,kBAAkB;gBAClB,SAAS,EAAE,SAAS;gBACpB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACjB,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,qBAAqB,EAAE,KAAK,CAAC,CAAA;YACxD,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;YAAS,CAAC;QACT,WAAW,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,UAAiE,EAAE;IAEnE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,6BAA6B,CAAA;IACpE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,CAAA;YACd,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,yBAAyB,CAAA;QACpE,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAA;YACb,OAAM;QACR,CAAC;QAED,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,OAAO,GAAkB,IAAI,CAAA;QACjC,IAAI,WAAW,GAAwB,IAAI,CAAA;QAC3C,IAAI,OAAO,GAAwB,IAAI,CAAA;QAEvC,MAAM,MAAM,GAAG,CAAC,KAAc,EAAE,EAAE;YAChC,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,IAAI,OAAO;gBAAE,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;YAC1C,IAAI,WAAW;gBAAE,WAAW,EAAE,CAAA;YAC9B,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM;gBAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YACnF,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC,CAAA;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC7B,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACnD,CAAC;QAED,WAAW,GAAG,eAAe,CAAC,SAAS,CACrC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,yBAAyB,EAClC,CAAC,SAAS,EAAE,EAAE;YACZ,IAAI,SAAS;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAA;QAC7B,CAAC,CACF,CAAA;QAED,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;YAChC,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,yBAAyB,CAAC,CAAA;QAC9D,CAAC,EAAE,SAAS,CAAC,CAAA;IACf,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,SAAS,EAAE,GAA6B,EAAE,CAAC,gBAAgB,EAAE;IAC7D,WAAW,EAAE,CAAC,EAAiD,EAAgB,EAAE;QAC/E,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC,CAAA;QAC7C,OAAO,eAAe,CAAC,SAAS,CAAC,GAAG,EAAE;YACpC,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAA;YAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAChC,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAM;YACxB,IAAI,GAAG,GAAG,CAAA;YACV,EAAE,CAAC,IAAI,CAAC,CAAA;QACV,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,qFAAqF;IACrF,WAAW,EAAE,CAAC,OAAoE,EAAW,EAAE,CAC7F,kBAAkB,CAAC,OAAO,CAAC;IAC7B,qFAAqF;IACrF,aAAa,EAAE,CACb,OAAoE,EACpE,EAA4B,EACd,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,EAAE,CAAC;IAEpD,gFAAgF;IAChF,yEAAyE;IACzE,uFAAuF;IACvF,QAAQ,EAAE,GAAqB,EAAE,CAAC,eAAe,CAAC,sBAAsB,EAAE,CAAC;IAC3E;iCAC6B;IAC7B,UAAU,EAAE,CAAC,EAAwC,EAAgB,EAAE,CACrE,wBAAwB,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvE;gFAC4E;IAC5E,oBAAoB,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC;IAEpE,kHAAkH;IAClH,IAAI,EAAE,CAAC,KAAkB,EAAiB,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC;IAC1E,+CAA+C;IAC/C,QAAQ,EAAE,GAAY,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS;IAC1D,iGAAiG;IACjG,UAAU,EAAE,CAAC,EAA+B,EAAgB,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;IAC7G,sGAAsG;IACtG,gBAAgB,EAAE,GAAY,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,gBAAgB;IACzE,iHAAiH;IACjH,kBAAkB,EAAE,CAAC,EAAgC,EAAgB,EAAE,CACrE,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,EAAE,EAAE,CAAC;IACvD,2CAA2C;IAC3C,6EAA6E;IAC7E,6CAA6C;IAC7C,aAAa,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAC,GAAG,MAAM,EAAC,CAAC,CAAC;IACzF,6FAA6F;IAC7F,OAAO,EAAE,CAAC,EAA+B,EAAgB,EAAE;QACzD,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,CAAA;QAChE,OAAO,YAAY,CAAC,SAAS,CAAC,GAAG,EAAE;YACjC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAA;YACrD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;YACnC,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAM;YACxB,IAAI,GAAG,GAAG,CAAA;YACV,0EAA0E;YAC1E,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAC,GAAG,MAAM,EAAC,CAAC,CAAC,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;OAMG;IACH,IAAI,EAAE,KAAK,EAAE,MAAc,EAAE,OAAwB,EAAiB,EAAE;QACtE,MAAM,wBAAwB,EAAE,CAAA;QAChC,OAAO,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,EAAC,GAAG,OAAO,EAAE,aAAa,EAAE,KAAK,EAAC,CAAC,CAAA;IACzE,CAAC;IACD,6EAA6E;IAC7E,UAAU,EAAE,CAAC,MAAqB,EAAiB,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,MAAM,CAAC;IAC3F;;;;;;;;OAQG;IACH,yBAAyB,EAAE,CAAC,MAAc,EAAiB,EAAE,CAC3D,YAAY,CAAC,uBAAuB,CAAC,EAAC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAC,CAAC;IAClE;;;;;;;;;;;;;;;;;;OAkBG;IACH,cAAc,EAAE,KAAK,IAAmB,EAAE;QACxC,MAAM,gBAAgB,GAAG,MAAM,gBAAgB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACnE,IAAI,gBAAgB,IAAI,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;YAClF,0EAA0E;YAC1E,sEAAsE;YACtE,sDAAsD;YACtD,OAAO,CAAC,GAAG,CAAC,oFAAoF,CAAC,CAAA;YACjG,MAAM,YAAY,CAAC,QAAQ,EAAE,CAAA;YAC7B,OAAM;QACR,CAAC;QACD,IAAI,sBAAsB,EAAE,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/C,oEAAoE;YACpE,6DAA6D;YAC7D,qEAAqE;YACrE,gEAAgE;YAChE,OAAO,CAAC,GAAG,CAAC,oGAAoG,CAAC,CAAA;YACjH,MAAM,YAAY,CAAC,UAAU,EAAE,CAAA;YAC/B,MAAM,wBAAwB,EAAE,CAAA;YAChC,OAAM;QACR,CAAC;QACD,IAAI,gBAAgB,EAAE,CAAC;YACrB,oEAAoE;YACpE,oEAAoE;YACpE,mEAAmE;YACnE,OAAO,CAAC,GAAG,CAAC,gGAAgG,CAAC,CAAA;YAC7G,MAAM,YAAY,CAAC,UAAU,EAAE,CAAA;YAC/B,OAAM;QACR,CAAC;QACD,0EAA0E;QAC1E,wEAAwE;QACxE,+DAA+D;QAC/D,OAAO,CAAC,GAAG,CAAC,8FAA8F,CAAC,CAAA;QAC3G,MAAM,YAAY,CAAC,MAAM,EAAE,CAAA;IAC7B,CAAC;IAED;;;OAGG;IACH,wBAAwB,EAAE,GAAkB,EAAE,CAAC,YAAY,CAAC,uBAAuB,EAAE;IAErF,6DAA6D;IAC7D,aAAa,EAAE,CAAC,EAAqC,EAAgB,EAAE;QACrE,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;QACxD,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAA;IAC3B,CAAC;IACD,oFAAoF;IACpF,iBAAiB,EAAE,CAAC,EAAyC,EAAgB,EAAE;QAC7E,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAA;QAC7D,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAA;IAC3B,CAAC;IACD,sFAAsF;IACtF,YAAY,EAAE,yBAAyB;IACvC,uEAAuE;IACvE,uBAAuB;CACxB,CAAA","sourcesContent":["/**\n * pairing facade — `engine.pairing`: scan for nearby glasses + pair. Scan state\n * (searching + results) comes from the engine core store; scan/connect go over the\n * bluetooth-sdk; pair-failure / not-ready are the bluetooth-sdk events.\n *\n * (Connect-the-already-paired-default is `engine.glasses.connectDefault()`; this\n * facade is the first-time discovery + pair flow.)\n */\n// Internal btsdk surface — updateBluetoothSettings (the Bluetooth Classic\n// target hint) lives on the full surface, not the public entry (same reason\n// the glasses facade imports internal).\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {ConnectOptions, Device, DeviceModel, PairFailureEvent, GlassesNotReadyEvent} from \"@glassly/bluetooth-sdk\"\nimport {useCoreStore} from \"../stores/core\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {hasDefaultDevice} from \"../services/DeviceStoreHydration\"\nimport {\n markPendingSelection,\n projectPairingIdentity,\n subscribePairingIdentity,\n type IdentitySnapshot,\n} from \"../services/PairingIdentity\"\nimport {fixtureIdentity} from \"../services/ScreenshotFixture\"\nimport {isGlassesConnected, isGlassesLinkLayerBusy, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {\n logAutomaticReportSubmissionStatus,\n toAutomaticReportSubmissionStatus,\n type AutomaticReportSubmissionStatus,\n} from \"../services/AutomaticReportResult\"\nimport {pushAllBluetoothSettings} from \"../services/GlassesSettingsSync\"\nimport {isControllerModel, isControllerReady} from \"../services/controllers\"\nimport {submitAutomaticReport} from \"./reports\"\n\nimport {BgTimer} from \"../utils/timers\"\n\nexport type {IdentitySnapshot} from \"../services/PairingIdentity\"\n\nexport interface PairingReadyWaitOptions {\n deviceModel?: string\n deviceName?: string\n timeoutMs?: number\n route?: string\n signal?: AbortSignal\n}\n\nconst DEFAULT_PAIRING_BOOT_TIMEOUT_MS = 35_000\nconst DEFAULT_BT_CLASSIC_TIMEOUT_MS = 1_000\nconst DEFAULT_PAIRING_ROUTE = \"/pairing/loading\"\nconst LOG_TAG = \"PairingTimeoutReport\"\n\nfunction projectReadiness() {\n const s = useGlassesStore.getState()\n return {\n state: s.connection.state,\n connected: s.connection.state === \"connected\",\n fullyBooted: isGlassesReady(s.connection),\n bluetoothClassicConnected: s.bluetoothClassicConnected,\n nativeLinkBusy: isGlassesLinkLayerBusy(s.connection),\n }\n}\n\nfunction projectTargetReady(options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">): boolean {\n const {deviceModel, deviceName} = options\n const glasses = useGlassesStore.getState()\n\n // A controller target is ready once some driver of that model delivers input;\n // the scan's device name is the R1's ring id or the band's Bluetooth name.\n if (isControllerModel(deviceModel)) {\n return Object.values(glasses.controllers ?? {}).some(\n (status) =>\n status.model === deviceModel &&\n isControllerReady(status) &&\n (!deviceName || status.name === deviceName || status.id === deviceName),\n )\n }\n\n if (!isGlassesReady(glasses.connection)) return false\n const identity = projectPairingIdentity()\n if (!deviceModel && !deviceName) return true\n return (\n identity.kind === \"paired\" &&\n (!deviceModel || identity.model === deviceModel) &&\n (!deviceName || identity.name === deviceName)\n )\n}\n\nfunction subscribeTargetReady(\n options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">,\n cb: (ready: boolean) => void,\n): () => void {\n let last = projectTargetReady(options)\n const notify = () => {\n const ready = projectTargetReady(options)\n if (ready === last) return\n last = ready\n cb(ready)\n }\n const unsubscribeGlasses = useGlassesStore.subscribe(notify)\n const unsubscribeSettings = useSettingsStore.subscribe(notify)\n return () => {\n unsubscribeGlasses()\n unsubscribeSettings()\n }\n}\n\nasync function waitForTargetReady(options: PairingReadyWaitOptions, timeoutMs: number): Promise<boolean> {\n return new Promise((resolve) => {\n if (options.signal?.aborted) {\n resolve(false)\n return\n }\n if (projectTargetReady(options)) {\n resolve(true)\n return\n }\n\n let settled = false\n let timeout: number | null = null\n let unsubscribe: (() => void) | null = null\n let onAbort: (() => void) | null = null\n const finish = (ready: boolean) => {\n if (settled) return\n settled = true\n if (timeout) BgTimer.clearTimeout(timeout)\n if (unsubscribe) unsubscribe()\n if (onAbort && options.signal) options.signal.removeEventListener(\"abort\", onAbort)\n resolve(ready)\n }\n\n unsubscribe = subscribeTargetReady(options, (ready) => {\n if (ready) finish(true)\n })\n if (options.signal) {\n onAbort = () => finish(false)\n options.signal.addEventListener(\"abort\", onAbort)\n }\n timeout = BgTimer.setTimeout(() => finish(projectTargetReady(options)), timeoutMs)\n })\n}\n\nexport type PairingReadinessSnapshot = ReturnType<typeof projectReadiness>\n\nexport async function submitPairingBootTimeoutReport(params: {\n deviceModel?: string\n deviceName?: string\n showGlassesBooting: boolean\n elapsedMs: number\n route?: string\n}): Promise<AutomaticReportSubmissionStatus> {\n const {deviceModel, deviceName, showGlassesBooting, elapsedMs, route = DEFAULT_PAIRING_ROUTE} = params\n const result = await submitAutomaticReport({\n kind: \"automatic\",\n trigger: {\n type: \"automatic\",\n source: \"pairing_loading\",\n reason: \"glasses_connect_timeout\",\n },\n report: {\n expectedBehavior: `Glasses should connect successfully within ${Math.round(elapsedMs / 1000)} seconds.`,\n actualBehavior: JSON.stringify(\n {\n deviceModel,\n deviceName,\n showGlassesBooting,\n elapsedMs,\n route,\n },\n null,\n 2,\n ),\n systemPriority: \"medium\",\n },\n throttleKey: `pairing_timeout|${deviceModel || \"unknown\"}|${deviceName || \"unknown\"}`,\n })\n\n const status = toAutomaticReportSubmissionStatus(result)\n logAutomaticReportSubmissionStatus(LOG_TAG, status, deviceModel, deviceName)\n return status\n}\n\nasync function waitForReadyDuringPairing(options: PairingReadyWaitOptions = {}): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_PAIRING_BOOT_TIMEOUT_MS\n let showGlassesBooting = false\n const notReadySub = BluetoothSdk.addListener(\"glasses_not_ready\", () => {\n showGlassesBooting = true\n })\n\n try {\n const ready = await waitForTargetReady(options, timeoutMs)\n\n if (!ready && !options.signal?.aborted) {\n void submitPairingBootTimeoutReport({\n deviceModel: options.deviceModel,\n deviceName: options.deviceName,\n showGlassesBooting,\n elapsedMs: timeoutMs,\n route: options.route,\n }).catch((error) => {\n console.error(`[${LOG_TAG}] Unexpected error:`, error)\n })\n }\n\n return ready\n } finally {\n notReadySub.remove()\n }\n}\n\nasync function waitForBluetoothClassic(\n options: Pick<PairingReadyWaitOptions, \"timeoutMs\" | \"signal\"> = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_BT_CLASSIC_TIMEOUT_MS\n return new Promise((resolve) => {\n if (options.signal?.aborted) {\n resolve(false)\n return\n }\n\n const initial = useGlassesStore.getState().bluetoothClassicConnected\n if (initial) {\n resolve(true)\n return\n }\n\n let settled = false\n let timeout: number | null = null\n let unsubscribe: (() => void) | null = null\n let onAbort: (() => void) | null = null\n\n const finish = (value: boolean) => {\n if (settled) return\n settled = true\n if (timeout) BgTimer.clearTimeout(timeout)\n if (unsubscribe) unsubscribe()\n if (onAbort && options.signal) options.signal.removeEventListener(\"abort\", onAbort)\n resolve(value)\n }\n\n if (options.signal) {\n onAbort = () => finish(false)\n options.signal.addEventListener(\"abort\", onAbort)\n }\n\n unsubscribe = useGlassesStore.subscribe(\n (s) => s.bluetoothClassicConnected,\n (connected) => {\n if (connected) finish(true)\n },\n )\n\n timeout = BgTimer.setTimeout(() => {\n finish(useGlassesStore.getState().bluetoothClassicConnected)\n }, timeoutMs)\n })\n}\n\nexport const pairing = {\n readiness: (): PairingReadinessSnapshot => projectReadiness(),\n onReadiness: (cb: (readiness: PairingReadinessSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectReadiness())\n return useGlassesStore.subscribe(() => {\n const snap = projectReadiness()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n /** Whether the exact wearable or controller selected on the scan screen is ready. */\n targetReady: (options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">): boolean =>\n projectTargetReady(options),\n /** Subscribe to readiness for the exact selected target, excluding stale devices. */\n onTargetReady: (\n options: Pick<PairingReadyWaitOptions, \"deviceModel\" | \"deviceName\">,\n cb: (ready: boolean) => void,\n ): (() => void) => subscribeTargetReady(options, cb),\n\n // --- pairing-identity lifecycle (the PairingIdentity read-model + the JS-owned\n // identity writes; promotion to `paired` only ever happens natively) ---\n /** The identity lifecycle snapshot: none | pending (chosen, never paired) | paired. */\n identity: (): IdentitySnapshot => fixtureIdentity(projectPairingIdentity()),\n /** Subscribe to identity changes; fires only when the projected snapshot changes.\n * Returns an unsubscribe. */\n onIdentity: (cb: (identity: IdentitySnapshot) => void): (() => void) =>\n subscribePairingIdentity((identity) => cb(fixtureIdentity(identity))),\n /** Mark the chosen model as the pending selection (the scan-entry write); the\n * host renders it as a finish-pairing affordance until pairing succeeds. */\n markPendingSelection: (model: string) => markPendingSelection(model),\n\n /** Start scanning for nearby glasses or controllers of `model`. Results land on `searchResults()`/`onFound()`. */\n scan: (model: DeviceModel): Promise<void> => BluetoothSdk.startScan(model),\n /** Whether a scan is currently in progress. */\n scanning: (): boolean => useCoreStore.getState().searching,\n /** Subscribe to scan-in-progress changes; fires only when it changes. Returns an unsubscribe. */\n onScanning: (cb: (scanning: boolean) => void): (() => void) => useCoreStore.subscribe((s) => s.searching, cb),\n /** Whether the phone has another Bluetooth (audio) device connected — the BT Classic pairing hint. */\n otherBtConnected: (): boolean => useCoreStore.getState().otherBtConnected,\n /** Subscribe to other-Bluetooth-device-connected changes; fires only when it changes. Returns an unsubscribe. */\n onOtherBtConnected: (cb: (connected: boolean) => void): (() => void) =>\n useCoreStore.subscribe((s) => s.otherBtConnected, cb),\n /** The current scan results (snapshot). */\n // Copy the array AND each entry: neither the list nor the Device objects may\n // leak as mutable references into the store.\n searchResults: () => useCoreStore.getState().searchResults.map((result) => ({...result})),\n /** Subscribe to scan-result changes; fires only when they change. Returns an unsubscribe. */\n onFound: (cb: (results: Device[]) => void): (() => void) => {\n let last = JSON.stringify(useCoreStore.getState().searchResults)\n return useCoreStore.subscribe(() => {\n const results = useCoreStore.getState().searchResults\n const key = JSON.stringify(results)\n if (key === last) return\n last = key\n // Copy like searchResults(): listeners must not mutate shared scan state.\n cb(results.map((result) => ({...result})))\n })\n },\n\n /**\n * Pair with (connect to) a discovered device. Two-phase identity: the connect\n * attempt only marks the device as pending (`saveAsDefault: false` — no eager\n * default-device write); the native layer promotes it to the default wearable\n * when pairing actually succeeds (handleDeviceReady), so an abandoned or\n * failed attempt can't leave a default identity with no paired device behind.\n */\n pair: async (device: Device, options?: ConnectOptions): Promise<void> => {\n await pushAllBluetoothSettings()\n return BluetoothSdk.connect(device, {...options, saveAsDefault: false})\n },\n /** Set a device as the default for subsequent `glasses.connectDefault()`. */\n setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),\n /**\n * Prime the native Bluetooth Classic audio watcher with the picked device.\n * The iOS Mentra Live flow pairs Classic audio BEFORE any BLE connect\n * exists, and native detects the pairing by matching the connected audio\n * route against its device_name — which two-phase identity no longer sets\n * at selection time. This is native-only routing state (device_name has no\n * native→JS echo): the phone's persisted identity is untouched, and\n * promotion still happens only at pairing success.\n */\n setBluetoothClassicTarget: (device: Device): Promise<void> =>\n BluetoothSdk.updateBluetoothSettings({device_name: device.name}),\n /**\n * Abandon an in-flight pairing attempt (back-out, failure retry, conflict\n * retry) without destroying an existing pairing. The decision comes from the\n * LIVE hydrated default-device read — never from flow-entry state, because a\n * pairing can PROMOTE while the flow is open (the glasses finish pairing\n * even as the user backs out of the UI), and an entry snapshot would forget\n * that brand-new pairing:\n * - Default device exists and glasses are connected (nothing in flight — an\n * attempt drops the link first): stop the scan, touch nothing else.\n * - Default device exists with an attempt in flight: cancel it, then re-seed\n * the native identity from the phone's persisted settings — the attempt's\n * connect-by-name overwrote the native device_name, so a later\n * connectDefault() would otherwise target the abandoned device.\n * - No default device (genuinely unpaired attempt): also forget, clearing\n * the partial native pairing state.\n * The read fails OPEN to \"preserve\" — a transient failure must never wipe a\n * real pairing. The `pending_wearable` marker is deliberately left alone —\n * the host renders it as a finish-pairing affordance.\n */\n abandonAttempt: async (): Promise<void> => {\n const nativeHasDefault = await hasDefaultDevice().catch(() => true)\n if (nativeHasDefault && isGlassesConnected(useGlassesStore.getState().connection)) {\n // Still connected means no connect attempt is in flight (an attempt drops\n // the existing link first): the user browsed the scan and backed out.\n // Stop the scan and leave the live pairing untouched.\n console.log(\"PairingIdentity: abandonAttempt — pairing intact and connected; stopping scan only\")\n await BluetoothSdk.stopScan()\n return\n }\n if (projectPairingIdentity().kind === \"paired\") {\n // The persisted settings describe a COMPLETE pairing: restore it to\n // native (the attempt's connect-by-name overwrote the native\n // device_name; and if native somehow lost its default entirely, this\n // repairs the divergence instead of forgetting a real pairing).\n console.log(\"PairingIdentity: abandonAttempt — preserving pairing; attempt cancelled, native identity re-seeded\")\n await BluetoothSdk.disconnect()\n await pushAllBluetoothSettings()\n return\n }\n if (nativeHasDefault) {\n // Mid-relay: native promoted and its echoes are still landing — the\n // incomplete JS snapshot must not be pushed over the fresher native\n // identity (the on-connect replay's race). Native holds the truth.\n console.log(\"PairingIdentity: abandonAttempt — preserving pairing; JS identity mid-relay, native kept as-is\")\n await BluetoothSdk.disconnect()\n return\n }\n // Partial attempt: forget owns teardown. Do NOT disconnect() first — that\n // nulls the MentraLive SGC and used to skip Classic removeBond, leaving\n // IBRT ACL up so glasses never re-advertise for the next scan.\n console.log(\"PairingIdentity: abandonAttempt — no pairing on either layer; forgetting the partial attempt\")\n await BluetoothSdk.forget()\n },\n\n /**\n * Abandon an in-flight controller pairing (back-out, failure retry). Only the\n * attempt's driver is dropped; paired controllers and the glasses are untouched.\n */\n abandonControllerAttempt: (): Promise<void> => BluetoothSdk.cancelControllerAttempt(),\n\n /** Subscribe to pairing failures; returns an unsubscribe. */\n onPairFailure: (cb: (event: PairFailureEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"pair_failure\", cb)\n return () => sub.remove()\n },\n /** Subscribe to glasses-not-ready events during pairing; returns an unsubscribe. */\n onGlassesNotReady: (cb: (event: GlassesNotReadyEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"glasses_not_ready\", cb)\n return () => sub.remove()\n },\n /** Wait for paired glasses to finish booting; engine files the timeout diagnostic. */\n waitForReady: waitForReadyDuringPairing,\n /** Wait briefly for Bluetooth Classic to come up after BLE pairing. */\n waitForBluetoothClassic,\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const ENGINE_RELEASE_METADATA = Object.freeze({
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"familyBaseVersion": "0.3.5",
|
|
4
|
-
"releaseIdentity": "0.3.5-dev.
|
|
5
|
-
"releaseSetId": "glassly-0.3.5-dev.
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"releaseIdentity": "0.3.5-dev.843",
|
|
5
|
+
"releaseSetId": "glassly-0.3.5-dev.843",
|
|
6
|
+
"sourceCommit": "85bd06271a34f9bb21c4dc7b3c75cb4393c08aba",
|
|
7
7
|
"otaManifestUrl": null,
|
|
8
8
|
"otaManifestSha256": null
|
|
9
9
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;CAC1B,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"0.3.5\",\n \"releaseIdentity\": \"0.3.5-dev.
|
|
1
|
+
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;CAC1B,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"0.3.5\",\n \"releaseIdentity\": \"0.3.5-dev.843\",\n \"releaseSetId\": \"glassly-0.3.5-dev.843\",\n \"sourceCommit\": \"85bd06271a34f9bb21c4dc7b3c75cb4393c08aba\",\n \"otaManifestUrl\": null,\n \"otaManifestSha256\": null\n})\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot fixture — dresses Phone Mode up as a paired Even Realities G2
|
|
3
|
+
* with a Meta Neural Band and an R1 ring, for the store-screenshot build only
|
|
4
|
+
* (EXPO_PUBLIC_SCREENSHOT_MODE=1, set by scripts/maestro-screenshots-*.mjs
|
|
5
|
+
* for its own Release build and never by a shipped one).
|
|
6
|
+
*
|
|
7
|
+
* The fixture is applied where hosts READ — engine.pairing.identity and
|
|
8
|
+
* engine.glasses.status/info/controllers.* — not where native writes: the
|
|
9
|
+
* Simulated driver keeps running underneath (so the mirror has frames),
|
|
10
|
+
* nothing is persisted, and native never learns of the fixture, so no
|
|
11
|
+
* reconnect, demotion or driver path can act on it. It is active only while
|
|
12
|
+
* the underlying identity is the simulated device: home enters Phone Mode as
|
|
13
|
+
* usual (identity `none` passes through untouched), the simulated pairing
|
|
14
|
+
* then flips the read-models to the fixture, and real glasses on a fixture
|
|
15
|
+
* build stay real.
|
|
16
|
+
*/
|
|
17
|
+
import type { ControllerStatus, PairedController } from "@glassly/bluetooth-sdk";
|
|
18
|
+
import { DeviceTypes } from "../types/enums";
|
|
19
|
+
import type { IdentitySnapshot } from "./PairingIdentity";
|
|
20
|
+
export declare function isScreenshotMode(): boolean;
|
|
21
|
+
export declare const FIXTURE_GLASSES: {
|
|
22
|
+
readonly model: DeviceTypes.G2;
|
|
23
|
+
readonly name: "Even G2_7F3A";
|
|
24
|
+
readonly battery: 87;
|
|
25
|
+
readonly caseBattery: 100;
|
|
26
|
+
readonly signal: -58;
|
|
27
|
+
};
|
|
28
|
+
interface FixtureController extends PairedController {
|
|
29
|
+
batteryLevel: number;
|
|
30
|
+
}
|
|
31
|
+
export declare const FIXTURE_CONTROLLERS: readonly FixtureController[];
|
|
32
|
+
/** The fixture dresses Phone Mode only. */
|
|
33
|
+
export declare function fixtureActive(identity: IdentitySnapshot): boolean;
|
|
34
|
+
export declare function fixtureIdentity(identity: IdentitySnapshot): IdentitySnapshot;
|
|
35
|
+
interface StatusLike {
|
|
36
|
+
battery: number;
|
|
37
|
+
charging: boolean;
|
|
38
|
+
case: {
|
|
39
|
+
battery: number;
|
|
40
|
+
charging: boolean;
|
|
41
|
+
open: boolean;
|
|
42
|
+
removed: boolean;
|
|
43
|
+
};
|
|
44
|
+
signal: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function fixtureStatus<T extends StatusLike>(status: T, identity: IdentitySnapshot): T;
|
|
47
|
+
interface InfoLike {
|
|
48
|
+
model: string;
|
|
49
|
+
firmwareUpdateAvailable: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare function fixtureInfo<T extends InfoLike>(info: T, identity: IdentitySnapshot): T;
|
|
52
|
+
/** Fixture controllers first, then any real records that are not the fixture's. */
|
|
53
|
+
export declare function fixtureControllerList(list: PairedController[], identity: IdentitySnapshot): PairedController[];
|
|
54
|
+
export declare function fixtureControllerStatuses(statuses: Record<string, ControllerStatus>, identity: IdentitySnapshot): Record<string, ControllerStatus>;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=ScreenshotFixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScreenshotFixture.d.ts","sourceRoot":"","sources":["../../src/services/ScreenshotFixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AAE9E,OAAO,EAAkB,WAAW,EAAC,MAAM,gBAAgB,CAAA;AAC3D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAA;AAEvD,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,eAAO,MAAM,eAAe;;;;;;CAMlB,CAAA;AAEV,UAAU,iBAAkB,SAAQ,gBAAgB;IAClD,YAAY,EAAE,MAAM,CAAA;CACrB;AAKD,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAG3D,CAAA;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAEjE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAG5E;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,CAAA;IAC3E,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,CAS5F;AAED,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,uBAAuB,EAAE,OAAO,CAAA;CACjC;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,CAGtF;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAK9G;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC1C,QAAQ,EAAE,gBAAgB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAkBlC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ControllerTypes, DeviceTypes } from "../types/enums";
|
|
2
|
+
export function isScreenshotMode() {
|
|
3
|
+
return process.env.EXPO_PUBLIC_SCREENSHOT_MODE === "1";
|
|
4
|
+
}
|
|
5
|
+
export const FIXTURE_GLASSES = {
|
|
6
|
+
model: DeviceTypes.G2,
|
|
7
|
+
name: "Even G2_7F3A",
|
|
8
|
+
battery: 87,
|
|
9
|
+
caseBattery: 100,
|
|
10
|
+
signal: -58,
|
|
11
|
+
};
|
|
12
|
+
// The R1's id is its ring id (from the BLE name); the band's is an opaque
|
|
13
|
+
// peripheral id. Both are stable fixtures so flows can target
|
|
14
|
+
// `controller-tile-<id>`.
|
|
15
|
+
export const FIXTURE_CONTROLLERS = [
|
|
16
|
+
{ id: "7C1A2B9E", model: ControllerTypes.NEURAL_BAND, name: "Meta Neural Band", batteryLevel: 73 },
|
|
17
|
+
{ id: "CEC5BA", model: ControllerTypes.R1, name: "CEC5BA", batteryLevel: 58 },
|
|
18
|
+
];
|
|
19
|
+
/** The fixture dresses Phone Mode only. */
|
|
20
|
+
export function fixtureActive(identity) {
|
|
21
|
+
return isScreenshotMode() && identity.kind === "paired" && identity.model.includes(DeviceTypes.SIMULATED);
|
|
22
|
+
}
|
|
23
|
+
export function fixtureIdentity(identity) {
|
|
24
|
+
if (!fixtureActive(identity))
|
|
25
|
+
return identity;
|
|
26
|
+
return { kind: "paired", model: FIXTURE_GLASSES.model, name: FIXTURE_GLASSES.name };
|
|
27
|
+
}
|
|
28
|
+
export function fixtureStatus(status, identity) {
|
|
29
|
+
if (!fixtureActive(identity))
|
|
30
|
+
return status;
|
|
31
|
+
return {
|
|
32
|
+
...status,
|
|
33
|
+
battery: FIXTURE_GLASSES.battery,
|
|
34
|
+
charging: false,
|
|
35
|
+
case: { battery: FIXTURE_GLASSES.caseBattery, charging: false, open: false, removed: false },
|
|
36
|
+
signal: FIXTURE_GLASSES.signal,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function fixtureInfo(info, identity) {
|
|
40
|
+
if (!fixtureActive(identity))
|
|
41
|
+
return info;
|
|
42
|
+
return { ...info, model: FIXTURE_GLASSES.model, firmwareUpdateAvailable: false };
|
|
43
|
+
}
|
|
44
|
+
/** Fixture controllers first, then any real records that are not the fixture's. */
|
|
45
|
+
export function fixtureControllerList(list, identity) {
|
|
46
|
+
if (!fixtureActive(identity))
|
|
47
|
+
return list;
|
|
48
|
+
const fixture = FIXTURE_CONTROLLERS.map(({ id, model, name }) => ({ id, model, name }));
|
|
49
|
+
const ids = new Set(fixture.map((record) => record.id));
|
|
50
|
+
return [...fixture, ...list.filter((record) => !ids.has(record.id))];
|
|
51
|
+
}
|
|
52
|
+
export function fixtureControllerStatuses(statuses, identity) {
|
|
53
|
+
if (!fixtureActive(identity))
|
|
54
|
+
return statuses;
|
|
55
|
+
const result = { ...statuses };
|
|
56
|
+
for (const { id, model, name, batteryLevel } of FIXTURE_CONTROLLERS) {
|
|
57
|
+
result[id] = {
|
|
58
|
+
id,
|
|
59
|
+
model,
|
|
60
|
+
name,
|
|
61
|
+
connected: true,
|
|
62
|
+
fullyBooted: true,
|
|
63
|
+
searching: false,
|
|
64
|
+
batteryLevel,
|
|
65
|
+
charging: false,
|
|
66
|
+
macAddress: "",
|
|
67
|
+
signalStrength: -62,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ScreenshotFixture.js.map
|