@mentra/engine 3.1.0-dev.9 → 3.1.0-dev.91

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 (109) 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 +11 -6
  9. package/build/facades/ota.d.ts.map +1 -1
  10. package/build/facades/ota.js +9 -5
  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 +2 -2
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +1 -1
  21. package/build/index.js.map +1 -1
  22. package/build/react/MentraLiveOtaFlow.d.ts +8 -2
  23. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
  24. package/build/react/MentraLiveOtaFlow.js +256 -354
  25. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  26. package/build/react/index.d.ts +1 -0
  27. package/build/react/index.d.ts.map +1 -1
  28. package/build/react/index.js +1 -0
  29. package/build/react/index.js.map +1 -1
  30. package/build/react/useMentraLiveOta.d.ts +84 -0
  31. package/build/react/useMentraLiveOta.d.ts.map +1 -0
  32. package/build/react/useMentraLiveOta.js +627 -0
  33. package/build/react/useMentraLiveOta.js.map +1 -0
  34. package/build/services/BlobStore.d.ts +2 -0
  35. package/build/services/BlobStore.d.ts.map +1 -1
  36. package/build/services/BlobStore.js +63 -10
  37. package/build/services/BlobStore.js.map +1 -1
  38. package/build/services/GlassesStatusProjection.d.ts +15 -2
  39. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  40. package/build/services/GlassesStatusProjection.js +3 -3
  41. package/build/services/GlassesStatusProjection.js.map +1 -1
  42. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  43. package/build/services/HotspotOtaTransport.js +8 -7
  44. package/build/services/HotspotOtaTransport.js.map +1 -1
  45. package/build/services/OtaAutoChain.d.ts +9 -2
  46. package/build/services/OtaAutoChain.d.ts.map +1 -1
  47. package/build/services/OtaAutoChain.js +10 -2
  48. package/build/services/OtaAutoChain.js.map +1 -1
  49. package/build/services/OtaInstallCoordinator.d.ts +26 -3
  50. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  51. package/build/services/OtaInstallCoordinator.js +35 -13
  52. package/build/services/OtaInstallCoordinator.js.map +1 -1
  53. package/build/services/OtaService.d.ts.map +1 -1
  54. package/build/services/OtaService.js +1 -1
  55. package/build/services/OtaService.js.map +1 -1
  56. package/build/services/OtaUpdateCheckService.d.ts +4 -1
  57. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  58. package/build/services/OtaUpdateCheckService.js +21 -5
  59. package/build/services/OtaUpdateCheckService.js.map +1 -1
  60. package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
  61. package/build/services/asg/localNetworkTransport.js +13 -12
  62. package/build/services/asg/localNetworkTransport.js.map +1 -1
  63. package/build/services/glassesClockSync.js +1 -1
  64. package/build/services/glassesClockSync.js.map +1 -1
  65. package/build/services/otaInstallPolicy.d.ts +4 -0
  66. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  67. package/build/services/otaInstallPolicy.js +4 -0
  68. package/build/services/otaInstallPolicy.js.map +1 -1
  69. package/build/services/otaReleaseVersion.d.ts +10 -0
  70. package/build/services/otaReleaseVersion.d.ts.map +1 -0
  71. package/build/services/otaReleaseVersion.js +14 -0
  72. package/build/services/otaReleaseVersion.js.map +1 -0
  73. package/build/stores/appVisibility.d.ts +3 -0
  74. package/build/stores/appVisibility.d.ts.map +1 -0
  75. package/build/stores/appVisibility.js +3 -0
  76. package/build/stores/appVisibility.js.map +1 -0
  77. package/build/stores/apps.d.ts.map +1 -1
  78. package/build/stores/apps.js +7 -1
  79. package/build/stores/apps.js.map +1 -1
  80. package/build/stores/core.d.ts +3 -3
  81. package/build/stores/core.d.ts.map +1 -1
  82. package/build/stores/core.js.map +1 -1
  83. package/build/stores/glasses.d.ts +10 -4
  84. package/build/stores/glasses.d.ts.map +1 -1
  85. package/build/stores/glasses.js.map +1 -1
  86. package/package.json +13 -6
  87. package/src/bluetooth-sdk/ota-transport.ts +1 -0
  88. package/src/facades/ota.ts +19 -6
  89. package/src/facades/pairing.ts +88 -12
  90. package/src/generated/releaseMetadata.ts +5 -5
  91. package/src/index.ts +2 -0
  92. package/src/react/MentraLiveOtaFlow.tsx +378 -480
  93. package/src/react/index.ts +14 -0
  94. package/src/react/useMentraLiveOta.ts +793 -0
  95. package/src/services/BlobStore.ts +57 -10
  96. package/src/services/GlassesStatusProjection.ts +7 -6
  97. package/src/services/HotspotOtaTransport.ts +8 -7
  98. package/src/services/OtaAutoChain.ts +26 -2
  99. package/src/services/OtaInstallCoordinator.ts +37 -13
  100. package/src/services/OtaService.ts +1 -2
  101. package/src/services/OtaUpdateCheckService.ts +26 -8
  102. package/src/services/asg/localNetworkTransport.ts +20 -19
  103. package/src/services/glassesClockSync.ts +1 -1
  104. package/src/services/otaInstallPolicy.ts +4 -0
  105. package/src/services/otaReleaseVersion.ts +22 -0
  106. package/src/stores/appVisibility.ts +3 -0
  107. package/src/stores/apps.ts +7 -1
  108. package/src/stores/core.ts +4 -4
  109. package/src/stores/glasses.ts +13 -6
@@ -0,0 +1,627 @@
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 (isOtaAutoChainActive() && !otaSnapshot.ready) {
167
+ const remainingMs = otaAutoChainReconnectWaitRemaining();
168
+ if (remainingMs === null)
169
+ return;
170
+ reconnectTimeout = setTimeout(() => {
171
+ if (cancelled || myGeneration !== performCheckGenerationRef.current || ota.snapshot().ready)
172
+ return;
173
+ stopOtaAutoChain();
174
+ checkCompletedRef.current = true;
175
+ setCheckState("error");
176
+ }, remainingMs);
177
+ return;
178
+ }
179
+ if (!otaSnapshot.connected) {
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
+ }, [
295
+ checkGeneration,
296
+ continueApprovedChain,
297
+ otaSnapshot.connected,
298
+ otaSnapshot.ready,
299
+ otaSnapshot.wifiStatusKnown,
300
+ page,
301
+ runtimeReady,
302
+ ]);
303
+ useEffect(() => {
304
+ if (!runtimeReady ||
305
+ page !== "check" ||
306
+ (checkState !== "checking" && checkState !== "update_available") ||
307
+ !otaSnapshot.wifiStatusKnown ||
308
+ !isOtaAutoChainActive()) {
309
+ return;
310
+ }
311
+ const result = selectedCheckResultRef.current;
312
+ if (!result?.updateAvailable || !result.updateInfo)
313
+ return;
314
+ if (!otaSnapshot.wifiConnected && otaSnapshot.hotspotOtaVersion !== 1) {
315
+ setCheckState("update_available");
316
+ return;
317
+ }
318
+ continueApprovedChain(result);
319
+ }, [
320
+ checkState,
321
+ continueApprovedChain,
322
+ otaSnapshot.hotspotOtaVersion,
323
+ otaSnapshot.wifiConnected,
324
+ otaSnapshot.wifiStatusKnown,
325
+ page,
326
+ runtimeReady,
327
+ ]);
328
+ useEffect(() => {
329
+ if (!runtimeReady || page !== "progress")
330
+ return;
331
+ ota.installSession.attach();
332
+ return () => ota.installSession.detach();
333
+ }, [page, runtimeReady]);
334
+ const firmwareRestarting = page === "progress" &&
335
+ ((!installSnapshot.connected && installSnapshot.displayState === "restarting") ||
336
+ installSnapshot.versionChangePhase === "restarting");
337
+ useEffect(() => {
338
+ if (page !== "progress")
339
+ return;
340
+ onFirmwareRestartingChangeRef.current?.(firmwareRestarting, true);
341
+ }, [firmwareRestarting, page]);
342
+ useEffect(() => {
343
+ if (page !== "progress")
344
+ return;
345
+ return () => {
346
+ onFirmwareRestartingChangeRef.current?.(false, false);
347
+ };
348
+ }, [page]);
349
+ useEffect(() => {
350
+ if (page !== "progress" ||
351
+ installSnapshot.displayState !== "complete" ||
352
+ !installSnapshot.connected ||
353
+ !isOtaAutoChainActive() ||
354
+ autoChainAdvancedRef.current) {
355
+ return;
356
+ }
357
+ const timeout = setTimeout(async () => {
358
+ if (!isOtaAutoChainActive())
359
+ return;
360
+ autoChainAdvancedRef.current = true;
361
+ await ota.installSession.finish();
362
+ returnToCheck();
363
+ }, AUTO_CHAIN_COMPLETE_DELAY_MS);
364
+ return () => clearTimeout(timeout);
365
+ }, [installSnapshot.connected, installSnapshot.displayState, page, returnToCheck]);
366
+ const check = useCallback(() => {
367
+ setBatteryBlocked(false);
368
+ setOfferedReleaseTransition(null);
369
+ setCompletedReleaseTransition(null);
370
+ setCompletedUpdate(false);
371
+ setCompletedChangelogs([]);
372
+ setPage("check");
373
+ setCheckState("checking");
374
+ setCheckGeneration((generation) => generation + 1);
375
+ }, []);
376
+ const install = useCallback(() => {
377
+ if (installActionPendingRef.current || page !== "check")
378
+ return;
379
+ const result = selectedCheckResultRef.current;
380
+ if (!result) {
381
+ setErrorKind("network");
382
+ setCheckState("error");
383
+ return;
384
+ }
385
+ const snapshot = ota.snapshot();
386
+ if (snapshot.batteryLevel !== null && snapshot.batteryLevel < MINIMUM_OTA_BATTERY_LEVEL) {
387
+ setBatteryBlocked(true);
388
+ return;
389
+ }
390
+ if (!snapshot.wifiStatusKnown) {
391
+ check();
392
+ return;
393
+ }
394
+ if (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1) {
395
+ onOpenWifiSetupRef.current?.();
396
+ return;
397
+ }
398
+ ota.installSession.prepare(result);
399
+ setBatteryBlocked(false);
400
+ setCompletedUpdate(false);
401
+ setCompletedChangelogs([]);
402
+ if (updateFingerprint) {
403
+ const fromVersion = offeredReleaseTransition
404
+ ? offeredReleaseTransition.fromVersion
405
+ : currentReleaseVersion(snapshot.appVersion);
406
+ beginOtaAutoChain(updateFingerprint, isVersionChange, {
407
+ fromVersion,
408
+ toVersion: releaseRangeTargetVersion(result),
409
+ releaseVersion: result.releaseVersion,
410
+ });
411
+ }
412
+ navigateToProgress();
413
+ }, [check, isVersionChange, navigateToProgress, offeredReleaseTransition, page, updateFingerprint]);
414
+ useEffect(() => {
415
+ if (batteryBlocked &&
416
+ (otaSnapshot.batteryLevel === null || otaSnapshot.batteryLevel >= MINIMUM_OTA_BATTERY_LEVEL)) {
417
+ setBatteryBlocked(false);
418
+ }
419
+ }, [batteryBlocked, otaSnapshot.batteryLevel]);
420
+ const retryInstall = useCallback(() => {
421
+ if (page !== "progress")
422
+ return;
423
+ onFirmwareRestartingChangeRef.current?.(false, true);
424
+ ota.installSession.retry();
425
+ }, [page]);
426
+ const finish = useCallback(async () => {
427
+ if (page === "check") {
428
+ onFinishedRef.current?.();
429
+ return;
430
+ }
431
+ const restartStillInProgress = installSnapshot.displayState === "restarting" ||
432
+ installSnapshot.versionChangePhase === "restarting" ||
433
+ installSnapshot.versionChangePhase === "verifying";
434
+ if (restartStillInProgress)
435
+ return;
436
+ const requiresGlassesReboot = shouldRequireGlassesRebootForBesFailure(installSnapshot.otaStatus, installSnapshot.otaProgress, installSnapshot.errorMsg);
437
+ if (requiresGlassesReboot)
438
+ stopOtaAutoChain();
439
+ await ota.installSession.finish();
440
+ returnToCheck();
441
+ }, [installSnapshot, page, returnToCheck]);
442
+ const discard = useCallback(async () => {
443
+ if (page !== "progress")
444
+ return;
445
+ stopOtaAutoChain();
446
+ await ota.installSession.discard();
447
+ returnToCheck();
448
+ }, [page, returnToCheck]);
449
+ const openWifiSetup = useCallback(() => {
450
+ if (page === "progress")
451
+ stopOtaAutoChain();
452
+ onOpenWifiSetupRef.current?.();
453
+ }, [page]);
454
+ const state = useMemo(() => {
455
+ const hotspotSupported = otaSnapshot.hotspotOtaVersion === 1;
456
+ const canInstall = otaSnapshot.wifiStatusKnown && (otaSnapshot.wifiConnected || hotspotSupported);
457
+ const autoChainActive = isOtaAutoChainActive();
458
+ if (!runtimeReady) {
459
+ return {
460
+ screen: "initializing",
461
+ connected: otaSnapshot.connected,
462
+ batteryLevel: otaSnapshot.batteryLevel,
463
+ transport: null,
464
+ updateRequired: isUpdateRequired,
465
+ versionChange: isVersionChange,
466
+ versionChangeConverged: false,
467
+ versionChangePhase: null,
468
+ wifiConnected: otaSnapshot.wifiConnected,
469
+ wifiStatusKnown: otaSnapshot.wifiStatusKnown,
470
+ hotspotSupported,
471
+ hotspotPhase: "idle",
472
+ hotspotArtifactPercent: null,
473
+ phase: null,
474
+ step: null,
475
+ currentStep: null,
476
+ totalSteps: null,
477
+ progress: null,
478
+ installingApkOnly: false,
479
+ firmwareRestarting: false,
480
+ error: null,
481
+ canInstall: false,
482
+ canRetry: false,
483
+ canFinish: false,
484
+ canDismiss: false,
485
+ canDiscard: false,
486
+ canOpenWifiSetup: false,
487
+ continueDisabled: false,
488
+ completedUpdate: false,
489
+ releaseTransition: null,
490
+ changelogs: [],
491
+ };
492
+ }
493
+ if (page === "check") {
494
+ const wifiRequired = otaSnapshot.wifiStatusKnown && !otaSnapshot.wifiConnected && !hotspotSupported;
495
+ let screen;
496
+ if (checkState === "checking")
497
+ screen = autoChainActive ? "finishing" : "checking";
498
+ else if (checkState === "update_available") {
499
+ screen = wifiRequired ? "wifi_required" : batteryBlocked ? "battery_required" : "update_available";
500
+ }
501
+ else if (checkState === "no_update")
502
+ screen = "up_to_date";
503
+ else if (checkState === "dev_build")
504
+ screen = "dev_build";
505
+ else
506
+ screen = errorKind === "pin_unavailable" ? "update_info_unavailable" : "check_failed";
507
+ let error = null;
508
+ if (screen === "check_failed") {
509
+ error = {
510
+ code: "check_failed",
511
+ message: "Couldn't check for updates. Please check your connection and try again.",
512
+ };
513
+ }
514
+ else if (screen === "update_info_unavailable") {
515
+ error = { code: "update_info_unavailable", message: "Update information for this app version is unavailable." };
516
+ }
517
+ return {
518
+ screen,
519
+ connected: otaSnapshot.connected,
520
+ batteryLevel: otaSnapshot.batteryLevel,
521
+ transport: otaSnapshot.wifiConnected ? "wifi" : hotspotSupported ? "hotspot" : null,
522
+ updateRequired: isUpdateRequired,
523
+ versionChange: isVersionChange,
524
+ versionChangeConverged: false,
525
+ versionChangePhase: null,
526
+ wifiConnected: otaSnapshot.wifiConnected,
527
+ wifiStatusKnown: otaSnapshot.wifiStatusKnown,
528
+ hotspotSupported,
529
+ hotspotPhase: "idle",
530
+ hotspotArtifactPercent: null,
531
+ phase: null,
532
+ step: null,
533
+ currentStep: null,
534
+ totalSteps: null,
535
+ progress: null,
536
+ installingApkOnly: false,
537
+ firmwareRestarting: false,
538
+ error,
539
+ canInstall: screen === "update_available" && canInstall,
540
+ canRetry: screen === "check_failed",
541
+ canFinish: screen === "up_to_date" || screen === "dev_build" || screen === "update_info_unavailable",
542
+ canDismiss: (screen === "update_available" || screen === "wifi_required" || screen === "battery_required") &&
543
+ !isUpdateRequired &&
544
+ !autoChainActive,
545
+ canDiscard: false,
546
+ canOpenWifiSetup: screen === "wifi_required",
547
+ continueDisabled: false,
548
+ completedUpdate: screen === "up_to_date" && completedUpdate,
549
+ releaseTransition: screen === "update_available" || screen === "wifi_required"
550
+ ? autoChainActive
551
+ ? releaseTransitionFromRange(otaAutoChainReleaseRange())
552
+ : offeredReleaseTransition
553
+ : screen === "up_to_date"
554
+ ? completedReleaseTransition
555
+ : null,
556
+ changelogs: screen === "up_to_date" ? completedChangelogs : [],
557
+ };
558
+ }
559
+ const requiresGlassesReboot = shouldRequireGlassesRebootForBesFailure(installSnapshot.otaStatus, installSnapshot.otaProgress, installSnapshot.errorMsg);
560
+ const showChangeWifi = shouldShowChangeWifiForOtaDownloadFailure(installSnapshot.otaStatus, installSnapshot.otaProgress, installSnapshot.errorMsg);
561
+ const displayedError = requiresGlassesReboot
562
+ ? BES_INSTALL_RESTART_MESSAGE
563
+ : installSnapshot.errorMsg || getOtaErrorMessage(installSnapshot.otaStatus?.error);
564
+ const progressState = progressScreen(installSnapshot);
565
+ const screen = progressState === "complete" && autoChainActive ? "finishing" : progressState;
566
+ const error = screen === "failed"
567
+ ? {
568
+ code: requiresGlassesReboot ? "bes_restart_required" : "install_failed",
569
+ message: displayedError,
570
+ }
571
+ : null;
572
+ const totalSteps = installSnapshot.otaStatus?.totalSteps ?? null;
573
+ const changelogs = screen === "complete" ? releaseChangelogsForActiveChain() : [];
574
+ return {
575
+ screen,
576
+ connected: installSnapshot.connected,
577
+ batteryLevel: otaSnapshot.batteryLevel,
578
+ transport: installSnapshot.transport,
579
+ updateRequired: isUpdateRequired,
580
+ versionChange: installSnapshot.isVersionChange,
581
+ versionChangeConverged: installSnapshot.versionChangeConverged,
582
+ versionChangePhase: installSnapshot.versionChangePhase,
583
+ wifiConnected: otaSnapshot.wifiConnected,
584
+ wifiStatusKnown: otaSnapshot.wifiStatusKnown,
585
+ hotspotSupported,
586
+ hotspotPhase: installSnapshot.hotspotPhase,
587
+ hotspotArtifactPercent: installSnapshot.hotspotArtifactPercent,
588
+ phase: installSnapshot.otaStatus?.phase ?? installSnapshot.otaProgress?.stage ?? null,
589
+ step: installSnapshot.otaStatus?.stepType ?? null,
590
+ currentStep: installSnapshot.otaStatus?.currentStep ?? null,
591
+ totalSteps,
592
+ progress: installProgress(installSnapshot),
593
+ installingApkOnly: installSnapshot.otaStatus?.stepType === "apk" &&
594
+ installSnapshot.otaStatus.phase === "install" &&
595
+ totalSteps === 1,
596
+ firmwareRestarting,
597
+ error,
598
+ canInstall: false,
599
+ canRetry: screen === "failed" && !requiresGlassesReboot,
600
+ canFinish: screen === "complete" || (screen === "failed" && requiresGlassesReboot),
601
+ canDismiss: false,
602
+ canDiscard: screen === "disconnected",
603
+ canOpenWifiSetup: screen === "failed" && showChangeWifi,
604
+ continueDisabled: installSnapshot.continueButtonDisabled,
605
+ completedUpdate: false,
606
+ releaseTransition: releaseTransitionFromRange(otaAutoChainReleaseRange()),
607
+ changelogs,
608
+ };
609
+ }, [
610
+ checkState,
611
+ batteryBlocked,
612
+ completedChangelogs,
613
+ completedReleaseTransition,
614
+ completedUpdate,
615
+ errorKind,
616
+ firmwareRestarting,
617
+ installSnapshot,
618
+ isUpdateRequired,
619
+ isVersionChange,
620
+ otaSnapshot,
621
+ offeredReleaseTransition,
622
+ page,
623
+ runtimeReady,
624
+ ]);
625
+ return useMemo(() => ({ state, check, retryCheck: check, install, retryInstall, finish, discard, openWifiSetup }), [check, discard, finish, install, openWifiSetup, retryInstall, state]);
626
+ }
627
+ //# sourceMappingURL=useMentraLiveOta.js.map