@mentra/engine 3.1.0-dev.7 → 3.1.0-dev.70

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.
Files changed (103) hide show
  1. package/README.md +23 -5
  2. package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
  3. package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/ota-transport.js +2 -0
  5. package/build/bluetooth-sdk/ota-transport.js.map +1 -0
  6. package/build/engine.d.ts +5 -2
  7. package/build/engine.d.ts.map +1 -1
  8. package/build/facades/ota.d.ts +10 -6
  9. package/build/facades/ota.d.ts.map +1 -1
  10. package/build/facades/ota.js +7 -4
  11. package/build/facades/ota.js.map +1 -1
  12. package/build/facades/pairing.d.ts +4 -0
  13. package/build/facades/pairing.d.ts.map +1 -1
  14. package/build/facades/pairing.js +81 -7
  15. package/build/facades/pairing.js.map +1 -1
  16. package/build/generated/releaseMetadata.js +5 -5
  17. package/build/generated/releaseMetadata.js.map +1 -1
  18. package/build/index.d.ts +1 -1
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js.map +1 -1
  21. package/build/react/MentraLiveOtaFlow.d.ts +8 -2
  22. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
  23. package/build/react/MentraLiveOtaFlow.js +249 -354
  24. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  25. package/build/react/index.d.ts +1 -0
  26. package/build/react/index.d.ts.map +1 -1
  27. package/build/react/index.js +1 -0
  28. package/build/react/index.js.map +1 -1
  29. package/build/react/useMentraLiveOta.d.ts +84 -0
  30. package/build/react/useMentraLiveOta.d.ts.map +1 -0
  31. package/build/react/useMentraLiveOta.js +616 -0
  32. package/build/react/useMentraLiveOta.js.map +1 -0
  33. package/build/services/BlobStore.d.ts +2 -0
  34. package/build/services/BlobStore.d.ts.map +1 -1
  35. package/build/services/BlobStore.js +63 -10
  36. package/build/services/BlobStore.js.map +1 -1
  37. package/build/services/GlassesStatusProjection.d.ts +15 -2
  38. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  39. package/build/services/GlassesStatusProjection.js +3 -3
  40. package/build/services/GlassesStatusProjection.js.map +1 -1
  41. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  42. package/build/services/HotspotOtaTransport.js +8 -7
  43. package/build/services/HotspotOtaTransport.js.map +1 -1
  44. package/build/services/OtaAutoChain.d.ts +9 -2
  45. package/build/services/OtaAutoChain.d.ts.map +1 -1
  46. package/build/services/OtaAutoChain.js +10 -2
  47. package/build/services/OtaAutoChain.js.map +1 -1
  48. package/build/services/OtaInstallCoordinator.d.ts +24 -3
  49. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  50. package/build/services/OtaInstallCoordinator.js +14 -12
  51. package/build/services/OtaInstallCoordinator.js.map +1 -1
  52. package/build/services/OtaService.d.ts.map +1 -1
  53. package/build/services/OtaService.js +1 -1
  54. package/build/services/OtaService.js.map +1 -1
  55. package/build/services/OtaUpdateCheckService.d.ts +4 -1
  56. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  57. package/build/services/OtaUpdateCheckService.js +21 -5
  58. package/build/services/OtaUpdateCheckService.js.map +1 -1
  59. package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
  60. package/build/services/asg/localNetworkTransport.js +13 -12
  61. package/build/services/asg/localNetworkTransport.js.map +1 -1
  62. package/build/services/glassesClockSync.js +1 -1
  63. package/build/services/glassesClockSync.js.map +1 -1
  64. package/build/services/otaReleaseVersion.d.ts +10 -0
  65. package/build/services/otaReleaseVersion.d.ts.map +1 -0
  66. package/build/services/otaReleaseVersion.js +14 -0
  67. package/build/services/otaReleaseVersion.js.map +1 -0
  68. package/build/stores/appVisibility.d.ts +3 -0
  69. package/build/stores/appVisibility.d.ts.map +1 -0
  70. package/build/stores/appVisibility.js +3 -0
  71. package/build/stores/appVisibility.js.map +1 -0
  72. package/build/stores/apps.d.ts.map +1 -1
  73. package/build/stores/apps.js +7 -1
  74. package/build/stores/apps.js.map +1 -1
  75. package/build/stores/core.d.ts +3 -3
  76. package/build/stores/core.d.ts.map +1 -1
  77. package/build/stores/core.js.map +1 -1
  78. package/build/stores/glasses.d.ts +10 -4
  79. package/build/stores/glasses.d.ts.map +1 -1
  80. package/build/stores/glasses.js.map +1 -1
  81. package/package.json +13 -6
  82. package/src/bluetooth-sdk/ota-transport.ts +1 -0
  83. package/src/facades/ota.ts +17 -5
  84. package/src/facades/pairing.ts +88 -12
  85. package/src/generated/releaseMetadata.ts +5 -5
  86. package/src/index.ts +1 -0
  87. package/src/react/MentraLiveOtaFlow.tsx +370 -473
  88. package/src/react/index.ts +14 -0
  89. package/src/react/useMentraLiveOta.ts +781 -0
  90. package/src/services/BlobStore.ts +57 -10
  91. package/src/services/GlassesStatusProjection.ts +7 -6
  92. package/src/services/HotspotOtaTransport.ts +8 -7
  93. package/src/services/OtaAutoChain.ts +26 -2
  94. package/src/services/OtaInstallCoordinator.ts +16 -13
  95. package/src/services/OtaService.ts +1 -2
  96. package/src/services/OtaUpdateCheckService.ts +26 -8
  97. package/src/services/asg/localNetworkTransport.ts +20 -19
  98. package/src/services/glassesClockSync.ts +1 -1
  99. package/src/services/otaReleaseVersion.ts +22 -0
  100. package/src/stores/appVisibility.ts +3 -0
  101. package/src/stores/apps.ts +7 -1
  102. package/src/stores/core.ts +4 -4
  103. package/src/stores/glasses.ts +13 -6
@@ -0,0 +1,616 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { ota } from "../facades/ota";
3
+ import { beginOtaAutoChain, clearOtaAutoChainReconnectWait, isOtaAutoChainActive, otaAutoChainFingerprint, otaAutoChainReleaseRange, otaAutoChainReconnectWaitRemaining, stopOtaAutoChain, tryAdvanceOtaAutoChain, } from "../services/OtaAutoChain";
4
+ import { BES_INSTALL_RESTART_MESSAGE, getOtaErrorMessage, shouldRequireGlassesRebootForBesFailure, shouldShowChangeWifiForOtaDownloadFailure, } from "../services/OtaErrorMapping";
5
+ import { useEngineSnapshot } from "./useEngineSnapshot";
6
+ const AUTO_CHAIN_NETWORK_RETRY_DELAY_MS = 5000;
7
+ const AUTO_CHAIN_COMPLETE_DELAY_MS = 750;
8
+ export const MINIMUM_OTA_BATTERY_LEVEL = 25;
9
+ function releaseRangeTargetVersion(result) {
10
+ return result.releaseVersion ?? result.updateInfo?.versionName ?? result.latestVersionInfo?.versionName ?? null;
11
+ }
12
+ function currentReleaseVersion(appVersion) {
13
+ const version = appVersion?.trim();
14
+ return version || null;
15
+ }
16
+ function releaseTransitionFromRange(range) {
17
+ if (!range?.releaseVersion)
18
+ return null;
19
+ return { fromVersion: range.fromVersion, toVersion: range.releaseVersion };
20
+ }
21
+ function releaseChangelogsForActiveChain() {
22
+ const range = otaAutoChainReleaseRange();
23
+ if (!range?.toVersion)
24
+ return [];
25
+ try {
26
+ return ota.getReleaseChangelogs(range.fromVersion, range.toVersion);
27
+ }
28
+ catch {
29
+ try {
30
+ return ota.getReleaseChangelogs(null, range.toVersion);
31
+ }
32
+ catch {
33
+ return [];
34
+ }
35
+ }
36
+ }
37
+ function installProgress(snapshot) {
38
+ if (snapshot.displayState !== "updating")
39
+ return null;
40
+ const { otaStatus } = snapshot;
41
+ const isDownload = otaStatus?.phase === "download";
42
+ const totalSteps = otaStatus?.totalSteps ?? 1;
43
+ const rawPercent = isDownload
44
+ ? (otaStatus?.stepPercent ?? 0)
45
+ : totalSteps >= 2
46
+ ? (otaStatus?.overallPercent ?? 0)
47
+ : (otaStatus?.stepPercent ?? 0);
48
+ return Math.min(Math.max(rawPercent, snapshot.mtkInstallStallSimulatedPercent ?? 0, 0), 100);
49
+ }
50
+ function progressScreen(snapshot) {
51
+ if (snapshot.versionChangePhase === "restarting")
52
+ return "restarting";
53
+ if (snapshot.versionChangePhase === "verifying")
54
+ return "verifying";
55
+ switch (snapshot.displayState) {
56
+ case "starting":
57
+ return snapshot.hotspotPhase === "idle" || snapshot.hotspotPhase === "serving" ? "starting" : "preparing_hotspot";
58
+ case "updating":
59
+ return "updating";
60
+ case "restarting":
61
+ return "restarting";
62
+ case "complete":
63
+ return "complete";
64
+ case "failed":
65
+ return "failed";
66
+ default:
67
+ return "disconnected";
68
+ }
69
+ }
70
+ /**
71
+ * Headless Mentra Live OTA flow. Engine owns checking, hotspot staging,
72
+ * APK/MTK/BES sequencing, restart recovery, retries, and verification; the host
73
+ * renders semantic state and invokes these idempotent actions.
74
+ */
75
+ export function useMentraLiveOta(options = {}) {
76
+ const { initialPage = "check", initializeRuntime = true, onFinished, onFirmwareRestartingChange, onOpenWifiSetup, } = options;
77
+ const otaSnapshot = useEngineSnapshot(ota.snapshot, ota.onSnapshot);
78
+ const installSnapshot = useEngineSnapshot(ota.installSession.snapshot, ota.installSession.onSnapshot);
79
+ const [page, setPage] = useState(initialPage);
80
+ const [runtimeReady, setRuntimeReady] = useState(!initializeRuntime);
81
+ const [checkState, setCheckState] = useState("checking");
82
+ const [isUpdateRequired, setIsUpdateRequired] = useState(true);
83
+ const [isVersionChange, setIsVersionChange] = useState(false);
84
+ const [errorKind, setErrorKind] = useState("network");
85
+ const [updateFingerprint, setUpdateFingerprint] = useState(null);
86
+ const [offeredReleaseTransition, setOfferedReleaseTransition] = useState(null);
87
+ const [completedReleaseTransition, setCompletedReleaseTransition] = useState(null);
88
+ const [completedUpdate, setCompletedUpdate] = useState(false);
89
+ const [completedChangelogs, setCompletedChangelogs] = useState([]);
90
+ const [batteryBlocked, setBatteryBlocked] = useState(false);
91
+ const [checkGeneration, setCheckGeneration] = useState(0);
92
+ const performCheckGenerationRef = useRef(0);
93
+ const activeCheckKeyRef = useRef(null);
94
+ const checkStartedRef = useRef(false);
95
+ const checkCompletedRef = useRef(false);
96
+ const selectedCheckResultRef = useRef(null);
97
+ const autoChainAdvancedRef = useRef(false);
98
+ const installActionPendingRef = useRef(false);
99
+ const onFinishedRef = useRef(onFinished);
100
+ const onOpenWifiSetupRef = useRef(onOpenWifiSetup);
101
+ const onFirmwareRestartingChangeRef = useRef(onFirmwareRestartingChange);
102
+ onFinishedRef.current = onFinished;
103
+ onOpenWifiSetupRef.current = onOpenWifiSetup;
104
+ onFirmwareRestartingChangeRef.current = onFirmwareRestartingChange;
105
+ useEffect(() => {
106
+ if (!initializeRuntime) {
107
+ setRuntimeReady(true);
108
+ return;
109
+ }
110
+ let cancelled = false;
111
+ setRuntimeReady(false);
112
+ void ota.initialize().finally(() => {
113
+ if (!cancelled)
114
+ setRuntimeReady(true);
115
+ });
116
+ return () => {
117
+ cancelled = true;
118
+ };
119
+ }, [initializeRuntime]);
120
+ const returnToCheck = useCallback(() => {
121
+ installActionPendingRef.current = false;
122
+ autoChainAdvancedRef.current = false;
123
+ setCheckState("checking");
124
+ setPage("check");
125
+ setCheckGeneration((generation) => generation + 1);
126
+ }, []);
127
+ const navigateToProgress = useCallback(() => {
128
+ installActionPendingRef.current = true;
129
+ ota.clearProgress();
130
+ setPage("progress");
131
+ }, []);
132
+ const continueApprovedChain = useCallback((result) => {
133
+ if (installActionPendingRef.current || !isOtaAutoChainActive() || !result.updateInfo)
134
+ return false;
135
+ const snapshot = ota.snapshot();
136
+ if (!snapshot.wifiStatusKnown || (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1))
137
+ return false;
138
+ const admission = tryAdvanceOtaAutoChain(otaAutoChainFingerprint(result), result.updateInfo.isDowngrade === true, releaseRangeTargetVersion(result), result.releaseVersion);
139
+ if (!admission.advance)
140
+ return false;
141
+ checkCompletedRef.current = true;
142
+ ota.installSession.prepare(result);
143
+ navigateToProgress();
144
+ return true;
145
+ }, [navigateToProgress]);
146
+ useEffect(() => {
147
+ if (!runtimeReady || page !== "check")
148
+ return;
149
+ const MIN_DISPLAY_TIME_MS = 1100;
150
+ const MAX_WAIT_FOR_VERSION_INFO_MS = 10_000;
151
+ const checkKey = String(checkGeneration);
152
+ if (activeCheckKeyRef.current !== checkKey) {
153
+ activeCheckKeyRef.current = checkKey;
154
+ checkStartedRef.current = false;
155
+ checkCompletedRef.current = false;
156
+ selectedCheckResultRef.current = null;
157
+ installActionPendingRef.current = false;
158
+ setCheckState("checking");
159
+ }
160
+ const myGeneration = ++performCheckGenerationRef.current;
161
+ let cancelled = false;
162
+ let reconnectTimeout = null;
163
+ const performCheck = async () => {
164
+ if (checkCompletedRef.current)
165
+ return;
166
+ if (!otaSnapshot.connected) {
167
+ if (isOtaAutoChainActive()) {
168
+ const remainingMs = otaAutoChainReconnectWaitRemaining();
169
+ if (remainingMs === null)
170
+ return;
171
+ reconnectTimeout = setTimeout(() => {
172
+ if (cancelled || myGeneration !== performCheckGenerationRef.current || ota.snapshot().connected)
173
+ return;
174
+ stopOtaAutoChain();
175
+ checkCompletedRef.current = true;
176
+ setCheckState("error");
177
+ }, remainingMs);
178
+ return;
179
+ }
180
+ if (checkStartedRef.current) {
181
+ stopOtaAutoChain();
182
+ checkCompletedRef.current = true;
183
+ setCheckState("error");
184
+ return;
185
+ }
186
+ checkCompletedRef.current = true;
187
+ onFinishedRef.current?.();
188
+ return;
189
+ }
190
+ clearOtaAutoChainReconnectWait();
191
+ checkStartedRef.current = true;
192
+ const startTime = Date.now();
193
+ try {
194
+ const checkOptions = {
195
+ waitForBuildNumberMs: MAX_WAIT_FOR_VERSION_INFO_MS,
196
+ waitForBesVersionMs: 5000,
197
+ waitForMtkVersionMs: 2000,
198
+ refreshVersionInfo: true,
199
+ fixClockBeforeCheck: false,
200
+ };
201
+ let result = await ota.checkForUpdates(checkOptions);
202
+ if (cancelled || myGeneration !== performCheckGenerationRef.current)
203
+ return;
204
+ if (!result.hasCheckCompleted && result.checkFailureReason === "network" && isOtaAutoChainActive()) {
205
+ await new Promise((resolve) => setTimeout(resolve, AUTO_CHAIN_NETWORK_RETRY_DELAY_MS));
206
+ if (cancelled || myGeneration !== performCheckGenerationRef.current)
207
+ return;
208
+ result = await ota.checkForUpdates(checkOptions);
209
+ }
210
+ if (cancelled || myGeneration !== performCheckGenerationRef.current)
211
+ return;
212
+ selectedCheckResultRef.current = result;
213
+ await new Promise((resolve) => setTimeout(resolve, Math.max(0, MIN_DISPLAY_TIME_MS - (Date.now() - startTime))));
214
+ if (cancelled || myGeneration !== performCheckGenerationRef.current)
215
+ return;
216
+ if (result.skippedReason === "disconnected") {
217
+ stopOtaAutoChain();
218
+ checkCompletedRef.current = true;
219
+ setCheckState("error");
220
+ return;
221
+ }
222
+ if (result.skippedReason === "missing_build") {
223
+ checkCompletedRef.current = true;
224
+ if (isOtaAutoChainActive()) {
225
+ stopOtaAutoChain();
226
+ setCheckState("error");
227
+ }
228
+ else {
229
+ onFinishedRef.current?.();
230
+ }
231
+ return;
232
+ }
233
+ if (result.skippedReason === "dev_build") {
234
+ stopOtaAutoChain();
235
+ checkCompletedRef.current = true;
236
+ ota.clearUpdateAvailable();
237
+ setCheckState("dev_build");
238
+ return;
239
+ }
240
+ if (!result.hasCheckCompleted) {
241
+ stopOtaAutoChain();
242
+ checkCompletedRef.current = true;
243
+ setErrorKind(result.checkFailureReason === "pin_unavailable" ? "pin_unavailable" : "network");
244
+ setCheckState("error");
245
+ return;
246
+ }
247
+ if (result.updateAvailable && result.updateInfo) {
248
+ setIsUpdateRequired(result.isRequired);
249
+ setIsVersionChange(result.updateInfo.isDowngrade === true);
250
+ const fingerprint = otaAutoChainFingerprint(result);
251
+ setUpdateFingerprint(fingerprint);
252
+ if (!isOtaAutoChainActive()) {
253
+ const fromVersion = currentReleaseVersion(ota.snapshot().appVersion);
254
+ setOfferedReleaseTransition(result.releaseVersion ? { fromVersion, toVersion: result.releaseVersion } : null);
255
+ }
256
+ if (isOtaAutoChainActive() && !ota.snapshot().wifiStatusKnown) {
257
+ checkCompletedRef.current = true;
258
+ return;
259
+ }
260
+ if (continueApprovedChain(result))
261
+ return;
262
+ checkCompletedRef.current = true;
263
+ setCheckState("update_available");
264
+ return;
265
+ }
266
+ const completedApprovedSession = isOtaAutoChainActive();
267
+ if (completedApprovedSession) {
268
+ setCompletedChangelogs(releaseChangelogsForActiveChain());
269
+ setCompletedReleaseTransition(releaseTransitionFromRange(otaAutoChainReleaseRange()));
270
+ }
271
+ setCompletedUpdate(completedApprovedSession);
272
+ stopOtaAutoChain();
273
+ checkCompletedRef.current = true;
274
+ ota.clearUpdateAvailable();
275
+ setCheckState("no_update");
276
+ }
277
+ catch (error) {
278
+ console.error("OTA check failed:", error);
279
+ await new Promise((resolve) => setTimeout(resolve, Math.max(0, MIN_DISPLAY_TIME_MS - (Date.now() - startTime))));
280
+ if (cancelled || myGeneration !== performCheckGenerationRef.current)
281
+ return;
282
+ stopOtaAutoChain();
283
+ checkCompletedRef.current = true;
284
+ setErrorKind("network");
285
+ setCheckState("error");
286
+ }
287
+ };
288
+ void performCheck();
289
+ return () => {
290
+ cancelled = true;
291
+ if (reconnectTimeout)
292
+ clearTimeout(reconnectTimeout);
293
+ };
294
+ }, [checkGeneration, continueApprovedChain, otaSnapshot.connected, otaSnapshot.wifiStatusKnown, page, runtimeReady]);
295
+ useEffect(() => {
296
+ if (!runtimeReady ||
297
+ page !== "check" ||
298
+ (checkState !== "checking" && checkState !== "update_available") ||
299
+ !otaSnapshot.wifiStatusKnown ||
300
+ !isOtaAutoChainActive()) {
301
+ return;
302
+ }
303
+ const result = selectedCheckResultRef.current;
304
+ if (!result?.updateAvailable || !result.updateInfo)
305
+ return;
306
+ if (!otaSnapshot.wifiConnected && otaSnapshot.hotspotOtaVersion !== 1) {
307
+ setCheckState("update_available");
308
+ return;
309
+ }
310
+ continueApprovedChain(result);
311
+ }, [
312
+ checkState,
313
+ continueApprovedChain,
314
+ otaSnapshot.hotspotOtaVersion,
315
+ otaSnapshot.wifiConnected,
316
+ otaSnapshot.wifiStatusKnown,
317
+ page,
318
+ runtimeReady,
319
+ ]);
320
+ useEffect(() => {
321
+ if (!runtimeReady || page !== "progress")
322
+ return;
323
+ ota.installSession.attach();
324
+ return () => ota.installSession.detach();
325
+ }, [page, runtimeReady]);
326
+ const firmwareRestarting = page === "progress" &&
327
+ ((!installSnapshot.connected && installSnapshot.displayState === "restarting") ||
328
+ installSnapshot.versionChangePhase === "restarting");
329
+ useEffect(() => {
330
+ if (page !== "progress")
331
+ return;
332
+ onFirmwareRestartingChangeRef.current?.(firmwareRestarting, true);
333
+ }, [firmwareRestarting, page]);
334
+ useEffect(() => {
335
+ if (page !== "progress")
336
+ return;
337
+ return () => {
338
+ onFirmwareRestartingChangeRef.current?.(false, false);
339
+ };
340
+ }, [page]);
341
+ useEffect(() => {
342
+ if (page !== "progress" ||
343
+ installSnapshot.displayState !== "complete" ||
344
+ !installSnapshot.connected ||
345
+ !isOtaAutoChainActive() ||
346
+ autoChainAdvancedRef.current) {
347
+ return;
348
+ }
349
+ const timeout = setTimeout(async () => {
350
+ if (!isOtaAutoChainActive())
351
+ return;
352
+ autoChainAdvancedRef.current = true;
353
+ await ota.installSession.finish();
354
+ returnToCheck();
355
+ }, AUTO_CHAIN_COMPLETE_DELAY_MS);
356
+ return () => clearTimeout(timeout);
357
+ }, [installSnapshot.connected, installSnapshot.displayState, page, returnToCheck]);
358
+ const check = useCallback(() => {
359
+ setBatteryBlocked(false);
360
+ setOfferedReleaseTransition(null);
361
+ setCompletedReleaseTransition(null);
362
+ setCompletedUpdate(false);
363
+ setCompletedChangelogs([]);
364
+ setPage("check");
365
+ setCheckState("checking");
366
+ setCheckGeneration((generation) => generation + 1);
367
+ }, []);
368
+ const install = useCallback(() => {
369
+ if (installActionPendingRef.current || page !== "check")
370
+ return;
371
+ const result = selectedCheckResultRef.current;
372
+ if (!result) {
373
+ setErrorKind("network");
374
+ setCheckState("error");
375
+ return;
376
+ }
377
+ const snapshot = ota.snapshot();
378
+ if (snapshot.batteryLevel !== null && snapshot.batteryLevel < MINIMUM_OTA_BATTERY_LEVEL) {
379
+ setBatteryBlocked(true);
380
+ return;
381
+ }
382
+ if (!snapshot.wifiStatusKnown) {
383
+ check();
384
+ return;
385
+ }
386
+ if (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1) {
387
+ onOpenWifiSetupRef.current?.();
388
+ return;
389
+ }
390
+ ota.installSession.prepare(result);
391
+ setBatteryBlocked(false);
392
+ setCompletedUpdate(false);
393
+ setCompletedChangelogs([]);
394
+ if (updateFingerprint) {
395
+ const fromVersion = offeredReleaseTransition
396
+ ? offeredReleaseTransition.fromVersion
397
+ : currentReleaseVersion(snapshot.appVersion);
398
+ beginOtaAutoChain(updateFingerprint, isVersionChange, {
399
+ fromVersion,
400
+ toVersion: releaseRangeTargetVersion(result),
401
+ releaseVersion: result.releaseVersion,
402
+ });
403
+ }
404
+ navigateToProgress();
405
+ }, [check, isVersionChange, navigateToProgress, offeredReleaseTransition, page, updateFingerprint]);
406
+ useEffect(() => {
407
+ if (batteryBlocked &&
408
+ (otaSnapshot.batteryLevel === null || otaSnapshot.batteryLevel >= MINIMUM_OTA_BATTERY_LEVEL)) {
409
+ setBatteryBlocked(false);
410
+ }
411
+ }, [batteryBlocked, otaSnapshot.batteryLevel]);
412
+ const retryInstall = useCallback(() => {
413
+ if (page !== "progress")
414
+ return;
415
+ onFirmwareRestartingChangeRef.current?.(false, true);
416
+ ota.installSession.retry();
417
+ }, [page]);
418
+ const finish = useCallback(async () => {
419
+ if (page === "check") {
420
+ onFinishedRef.current?.();
421
+ return;
422
+ }
423
+ if (installSnapshot.displayState === "restarting")
424
+ stopOtaAutoChain();
425
+ const requiresGlassesReboot = shouldRequireGlassesRebootForBesFailure(installSnapshot.otaStatus, installSnapshot.otaProgress, installSnapshot.errorMsg);
426
+ if (requiresGlassesReboot)
427
+ stopOtaAutoChain();
428
+ await ota.installSession.finish();
429
+ returnToCheck();
430
+ }, [installSnapshot, page, returnToCheck]);
431
+ const discard = useCallback(async () => {
432
+ if (page !== "progress")
433
+ return;
434
+ stopOtaAutoChain();
435
+ await ota.installSession.discard();
436
+ returnToCheck();
437
+ }, [page, returnToCheck]);
438
+ const openWifiSetup = useCallback(() => {
439
+ if (page === "progress")
440
+ stopOtaAutoChain();
441
+ onOpenWifiSetupRef.current?.();
442
+ }, [page]);
443
+ const state = useMemo(() => {
444
+ const hotspotSupported = otaSnapshot.hotspotOtaVersion === 1;
445
+ const canInstall = otaSnapshot.wifiStatusKnown && (otaSnapshot.wifiConnected || hotspotSupported);
446
+ const autoChainActive = isOtaAutoChainActive();
447
+ if (!runtimeReady) {
448
+ return {
449
+ screen: "initializing",
450
+ connected: otaSnapshot.connected,
451
+ batteryLevel: otaSnapshot.batteryLevel,
452
+ transport: null,
453
+ updateRequired: isUpdateRequired,
454
+ versionChange: isVersionChange,
455
+ versionChangeConverged: false,
456
+ versionChangePhase: null,
457
+ wifiConnected: otaSnapshot.wifiConnected,
458
+ wifiStatusKnown: otaSnapshot.wifiStatusKnown,
459
+ hotspotSupported,
460
+ hotspotPhase: "idle",
461
+ hotspotArtifactPercent: null,
462
+ phase: null,
463
+ step: null,
464
+ currentStep: null,
465
+ totalSteps: null,
466
+ progress: null,
467
+ installingApkOnly: false,
468
+ firmwareRestarting: false,
469
+ error: null,
470
+ canInstall: false,
471
+ canRetry: false,
472
+ canFinish: false,
473
+ canDismiss: false,
474
+ canDiscard: false,
475
+ canOpenWifiSetup: false,
476
+ continueDisabled: false,
477
+ completedUpdate: false,
478
+ releaseTransition: null,
479
+ changelogs: [],
480
+ };
481
+ }
482
+ if (page === "check") {
483
+ const wifiRequired = otaSnapshot.wifiStatusKnown && !otaSnapshot.wifiConnected && !hotspotSupported;
484
+ let screen;
485
+ if (checkState === "checking")
486
+ screen = autoChainActive ? "finishing" : "checking";
487
+ else if (checkState === "update_available") {
488
+ screen = wifiRequired ? "wifi_required" : batteryBlocked ? "battery_required" : "update_available";
489
+ }
490
+ else if (checkState === "no_update")
491
+ screen = "up_to_date";
492
+ else if (checkState === "dev_build")
493
+ screen = "dev_build";
494
+ else
495
+ screen = errorKind === "pin_unavailable" ? "update_info_unavailable" : "check_failed";
496
+ let error = null;
497
+ if (screen === "check_failed") {
498
+ error = {
499
+ code: "check_failed",
500
+ message: "Couldn't check for updates. Please check your connection and try again.",
501
+ };
502
+ }
503
+ else if (screen === "update_info_unavailable") {
504
+ error = { code: "update_info_unavailable", message: "Update information for this app version is unavailable." };
505
+ }
506
+ return {
507
+ screen,
508
+ connected: otaSnapshot.connected,
509
+ batteryLevel: otaSnapshot.batteryLevel,
510
+ transport: otaSnapshot.wifiConnected ? "wifi" : hotspotSupported ? "hotspot" : null,
511
+ updateRequired: isUpdateRequired,
512
+ versionChange: isVersionChange,
513
+ versionChangeConverged: false,
514
+ versionChangePhase: null,
515
+ wifiConnected: otaSnapshot.wifiConnected,
516
+ wifiStatusKnown: otaSnapshot.wifiStatusKnown,
517
+ hotspotSupported,
518
+ hotspotPhase: "idle",
519
+ hotspotArtifactPercent: null,
520
+ phase: null,
521
+ step: null,
522
+ currentStep: null,
523
+ totalSteps: null,
524
+ progress: null,
525
+ installingApkOnly: false,
526
+ firmwareRestarting: false,
527
+ error,
528
+ canInstall: screen === "update_available" && canInstall,
529
+ canRetry: screen === "check_failed",
530
+ canFinish: screen === "up_to_date" || screen === "dev_build" || screen === "update_info_unavailable",
531
+ canDismiss: (screen === "update_available" || screen === "wifi_required" || screen === "battery_required") &&
532
+ !isUpdateRequired &&
533
+ !autoChainActive,
534
+ canDiscard: false,
535
+ canOpenWifiSetup: screen === "wifi_required",
536
+ continueDisabled: false,
537
+ completedUpdate: screen === "up_to_date" && completedUpdate,
538
+ releaseTransition: screen === "update_available" || screen === "wifi_required"
539
+ ? autoChainActive
540
+ ? releaseTransitionFromRange(otaAutoChainReleaseRange())
541
+ : offeredReleaseTransition
542
+ : screen === "up_to_date"
543
+ ? completedReleaseTransition
544
+ : null,
545
+ changelogs: screen === "up_to_date" ? completedChangelogs : [],
546
+ };
547
+ }
548
+ const requiresGlassesReboot = shouldRequireGlassesRebootForBesFailure(installSnapshot.otaStatus, installSnapshot.otaProgress, installSnapshot.errorMsg);
549
+ const showChangeWifi = shouldShowChangeWifiForOtaDownloadFailure(installSnapshot.otaStatus, installSnapshot.otaProgress, installSnapshot.errorMsg);
550
+ const displayedError = requiresGlassesReboot
551
+ ? BES_INSTALL_RESTART_MESSAGE
552
+ : installSnapshot.errorMsg || getOtaErrorMessage(installSnapshot.otaStatus?.error);
553
+ const progressState = progressScreen(installSnapshot);
554
+ const screen = progressState === "complete" && autoChainActive ? "finishing" : progressState;
555
+ const error = screen === "failed"
556
+ ? {
557
+ code: requiresGlassesReboot ? "bes_restart_required" : "install_failed",
558
+ message: displayedError,
559
+ }
560
+ : null;
561
+ const totalSteps = installSnapshot.otaStatus?.totalSteps ?? null;
562
+ const changelogs = screen === "complete" ? releaseChangelogsForActiveChain() : [];
563
+ return {
564
+ screen,
565
+ connected: installSnapshot.connected,
566
+ batteryLevel: otaSnapshot.batteryLevel,
567
+ transport: installSnapshot.transport,
568
+ updateRequired: isUpdateRequired,
569
+ versionChange: installSnapshot.isVersionChange,
570
+ versionChangeConverged: installSnapshot.versionChangeConverged,
571
+ versionChangePhase: installSnapshot.versionChangePhase,
572
+ wifiConnected: otaSnapshot.wifiConnected,
573
+ wifiStatusKnown: otaSnapshot.wifiStatusKnown,
574
+ hotspotSupported,
575
+ hotspotPhase: installSnapshot.hotspotPhase,
576
+ hotspotArtifactPercent: installSnapshot.hotspotArtifactPercent,
577
+ phase: installSnapshot.otaStatus?.phase ?? installSnapshot.otaProgress?.stage ?? null,
578
+ step: installSnapshot.otaStatus?.stepType ?? null,
579
+ currentStep: installSnapshot.otaStatus?.currentStep ?? null,
580
+ totalSteps,
581
+ progress: installProgress(installSnapshot),
582
+ installingApkOnly: installSnapshot.otaStatus?.stepType === "apk" &&
583
+ installSnapshot.otaStatus.phase === "install" &&
584
+ totalSteps === 1,
585
+ firmwareRestarting,
586
+ error,
587
+ canInstall: false,
588
+ canRetry: screen === "failed" && !requiresGlassesReboot,
589
+ canFinish: screen === "complete" || screen === "restarting" || (screen === "failed" && requiresGlassesReboot),
590
+ canDismiss: false,
591
+ canDiscard: screen === "disconnected",
592
+ canOpenWifiSetup: screen === "failed" && showChangeWifi,
593
+ continueDisabled: installSnapshot.continueButtonDisabled,
594
+ completedUpdate: false,
595
+ releaseTransition: releaseTransitionFromRange(otaAutoChainReleaseRange()),
596
+ changelogs,
597
+ };
598
+ }, [
599
+ checkState,
600
+ batteryBlocked,
601
+ completedChangelogs,
602
+ completedReleaseTransition,
603
+ completedUpdate,
604
+ errorKind,
605
+ firmwareRestarting,
606
+ installSnapshot,
607
+ isUpdateRequired,
608
+ isVersionChange,
609
+ otaSnapshot,
610
+ offeredReleaseTransition,
611
+ page,
612
+ runtimeReady,
613
+ ]);
614
+ return useMemo(() => ({ state, check, retryCheck: check, install, retryInstall, finish, discard, openWifiSetup }), [check, discard, finish, install, openWifiSetup, retryInstall, state]);
615
+ }
616
+ //# sourceMappingURL=useMentraLiveOta.js.map