@glassly/engine 0.1.0-dev.61 → 0.1.0-dev.89

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 (147) hide show
  1. package/README.md +6 -2
  2. package/build/engine.d.ts +7 -2
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +3 -0
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/display.d.ts +10 -3
  7. package/build/facades/display.d.ts.map +1 -1
  8. package/build/facades/display.js +11 -1
  9. package/build/facades/display.js.map +1 -1
  10. package/build/facades/displayMirror.d.ts +2 -6
  11. package/build/facades/displayMirror.d.ts.map +1 -1
  12. package/build/facades/displayMirror.js +7 -14
  13. package/build/facades/displayMirror.js.map +1 -1
  14. package/build/facades/glasses.d.ts +1 -0
  15. package/build/facades/glasses.d.ts.map +1 -1
  16. package/build/facades/glasses.js +2 -0
  17. package/build/facades/glasses.js.map +1 -1
  18. package/build/facades/ota.d.ts +3 -1
  19. package/build/facades/ota.d.ts.map +1 -1
  20. package/build/facades/ota.js +4 -2
  21. package/build/facades/ota.js.map +1 -1
  22. package/build/facades/phoneNotifications.d.ts +12 -2
  23. package/build/facades/phoneNotifications.d.ts.map +1 -1
  24. package/build/facades/phoneNotifications.js +30 -10
  25. package/build/facades/phoneNotifications.js.map +1 -1
  26. package/build/generated/releaseMetadata.js +3 -3
  27. package/build/generated/releaseMetadata.js.map +1 -1
  28. package/build/index.d.ts +2 -2
  29. package/build/index.d.ts.map +1 -1
  30. package/build/index.js +2 -2
  31. package/build/index.js.map +1 -1
  32. package/build/react/MentraLiveOtaFlow.d.ts +6 -1
  33. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
  34. package/build/react/MentraLiveOtaFlow.js +136 -14
  35. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  36. package/build/react/useMentraLiveOta.d.ts +5 -1
  37. package/build/react/useMentraLiveOta.d.ts.map +1 -1
  38. package/build/react/useMentraLiveOta.js +135 -58
  39. package/build/react/useMentraLiveOta.js.map +1 -1
  40. package/build/services/CustomDashboardService.d.ts +11 -10
  41. package/build/services/CustomDashboardService.d.ts.map +1 -1
  42. package/build/services/CustomDashboardService.js +70 -69
  43. package/build/services/CustomDashboardService.js.map +1 -1
  44. package/build/services/DashboardCoordinator.d.ts +26 -0
  45. package/build/services/DashboardCoordinator.d.ts.map +1 -0
  46. package/build/services/DashboardCoordinator.js +125 -0
  47. package/build/services/DashboardCoordinator.js.map +1 -0
  48. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  49. package/build/services/DeviceEventRouter.js +5 -7
  50. package/build/services/DeviceEventRouter.js.map +1 -1
  51. package/build/services/DisplayProcessor.d.ts +3 -3
  52. package/build/services/DisplayProcessor.js +3 -3
  53. package/build/services/DisplayProcessor.js.map +1 -1
  54. package/build/services/LocalDisplayManager.d.ts +12 -2
  55. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  56. package/build/services/LocalDisplayManager.js +68 -14
  57. package/build/services/LocalDisplayManager.js.map +1 -1
  58. package/build/services/OtaInstallCoordinator.d.ts +2 -0
  59. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  60. package/build/services/OtaInstallCoordinator.js +21 -1
  61. package/build/services/OtaInstallCoordinator.js.map +1 -1
  62. package/build/services/PhoneNotificationsSync.d.ts +13 -0
  63. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  64. package/build/services/PhoneNotificationsSync.js +101 -9
  65. package/build/services/PhoneNotificationsSync.js.map +1 -1
  66. package/build/services/PhonePhotoCoordinator.js +1 -1
  67. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  68. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  69. package/build/services/PhoneStreamCoordinator.js +9 -6
  70. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  71. package/build/services/SceneRenderer.d.ts.map +1 -1
  72. package/build/services/SceneRenderer.js +5 -0
  73. package/build/services/SceneRenderer.js.map +1 -1
  74. package/build/services/StreamLifecycleController.d.ts +4 -0
  75. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  76. package/build/services/StreamLifecycleController.js +4 -0
  77. package/build/services/StreamLifecycleController.js.map +1 -1
  78. package/build/services/effectiveCapabilities.d.ts +1 -1
  79. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  80. package/build/services/effectiveCapabilities.js +9 -4
  81. package/build/services/effectiveCapabilities.js.map +1 -1
  82. package/build/services/iosNotificationAppCatalog.d.ts +13 -0
  83. package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
  84. package/build/services/iosNotificationAppCatalog.js +28 -0
  85. package/build/services/iosNotificationAppCatalog.js.map +1 -0
  86. package/build/services/otaInstallPolicy.d.ts +4 -0
  87. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  88. package/build/services/otaInstallPolicy.js +4 -0
  89. package/build/services/otaInstallPolicy.js.map +1 -1
  90. package/build/stores/display.d.ts +4 -4
  91. package/build/stores/display.d.ts.map +1 -1
  92. package/build/stores/display.js +12 -36
  93. package/build/stores/display.js.map +1 -1
  94. package/build/stores/glasses.d.ts.map +1 -1
  95. package/build/stores/glasses.js +2 -0
  96. package/build/stores/glasses.js.map +1 -1
  97. package/build/stores/settings.d.ts.map +1 -1
  98. package/build/stores/settings.js +53 -2
  99. package/build/stores/settings.js.map +1 -1
  100. package/build/types/applet.d.ts +1 -1
  101. package/build/types/applet.js.map +1 -1
  102. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  103. package/build/types/capabilities/even-realities-g2.js +2 -2
  104. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  105. package/build/types/hardware.d.ts.map +1 -1
  106. package/build/types/hardware.js +1 -0
  107. package/build/types/hardware.js.map +1 -1
  108. package/build/types/index.d.ts +1 -1
  109. package/build/types/index.d.ts.map +1 -1
  110. package/build/types/index.js +1 -1
  111. package/build/types/index.js.map +1 -1
  112. package/build/utils/display/composer/ColumnComposer.d.ts +1 -1
  113. package/build/utils/display/composer/ColumnComposer.js +1 -1
  114. package/build/utils/display/composer/ColumnComposer.js.map +1 -1
  115. package/package.json +8 -7
  116. package/src/engine.ts +3 -0
  117. package/src/facades/display.ts +11 -1
  118. package/src/facades/displayMirror.ts +7 -15
  119. package/src/facades/glasses.ts +2 -0
  120. package/src/facades/ota.ts +4 -2
  121. package/src/facades/phoneNotifications.ts +32 -10
  122. package/src/generated/releaseMetadata.ts +3 -3
  123. package/src/index.ts +2 -1
  124. package/src/react/MentraLiveOtaFlow.tsx +226 -41
  125. package/src/react/useMentraLiveOta.ts +146 -60
  126. package/src/services/CustomDashboardService.ts +67 -66
  127. package/src/services/DashboardCoordinator.ts +121 -0
  128. package/src/services/DeviceEventRouter.ts +5 -7
  129. package/src/services/DisplayProcessor.ts +3 -3
  130. package/src/services/LocalDisplayManager.ts +68 -16
  131. package/src/services/OtaInstallCoordinator.ts +21 -0
  132. package/src/services/PhoneNotificationsSync.ts +125 -9
  133. package/src/services/PhonePhotoCoordinator.ts +1 -1
  134. package/src/services/PhoneStreamCoordinator.ts +10 -6
  135. package/src/services/SceneRenderer.ts +4 -0
  136. package/src/services/StreamLifecycleController.ts +4 -0
  137. package/src/services/effectiveCapabilities.ts +9 -4
  138. package/src/services/iosNotificationAppCatalog.ts +39 -0
  139. package/src/services/otaInstallPolicy.ts +4 -0
  140. package/src/stores/display.ts +16 -48
  141. package/src/stores/glasses.ts +2 -0
  142. package/src/stores/settings.ts +53 -2
  143. package/src/types/applet.ts +1 -1
  144. package/src/types/capabilities/even-realities-g2.ts +3 -2
  145. package/src/types/hardware.ts +1 -0
  146. package/src/types/index.ts +1 -1
  147. package/src/utils/display/composer/ColumnComposer.ts +1 -1
@@ -28,7 +28,9 @@ export type MentraLiveOtaFlowPage = "check" | "progress"
28
28
  export type MentraLiveOtaScreen =
29
29
  | "initializing"
30
30
  | "checking"
31
+ | "finishing"
31
32
  | "update_available"
33
+ | "battery_required"
32
34
  | "wifi_required"
33
35
  | "up_to_date"
34
36
  | "dev_build"
@@ -69,6 +71,7 @@ export type MentraLiveOtaReleaseTransition = {
69
71
  export type MentraLiveOtaState = {
70
72
  screen: MentraLiveOtaScreen
71
73
  connected: boolean
74
+ batteryLevel: number | null
72
75
  transport: MentraLiveOtaTransport | null
73
76
  updateRequired: boolean
74
77
  versionChange: boolean
@@ -94,6 +97,8 @@ export type MentraLiveOtaState = {
94
97
  canDiscard: boolean
95
98
  canOpenWifiSetup: boolean
96
99
  continueDisabled: boolean
100
+ /** True only after an approved update session reaches a final no-update check. */
101
+ completedUpdate: boolean
97
102
  /** Release labels for the offered or just-completed coordinated update. */
98
103
  releaseTransition: MentraLiveOtaReleaseTransition | null
99
104
  /** Release notes crossed by this update, newest first. Populated on completion. */
@@ -128,6 +133,7 @@ type CheckState = "checking" | "update_available" | "no_update" | "dev_build" |
128
133
 
129
134
  const AUTO_CHAIN_NETWORK_RETRY_DELAY_MS = 5000
130
135
  const AUTO_CHAIN_COMPLETE_DELAY_MS = 750
136
+ export const MINIMUM_OTA_BATTERY_LEVEL = 25
131
137
 
132
138
  function releaseRangeTargetVersion(result: OtaCheckCurrentGlassesResult): string | null {
133
139
  return result.releaseVersion ?? result.updateInfo?.versionName ?? result.latestVersionInfo?.versionName ?? null
@@ -163,10 +169,10 @@ function installProgress(snapshot: OtaInstallSnapshot): number | null {
163
169
  const isDownload = otaStatus?.phase === "download"
164
170
  const totalSteps = otaStatus?.totalSteps ?? 1
165
171
  const rawPercent = isDownload
166
- ? otaStatus?.stepPercent ?? 0
172
+ ? (otaStatus?.stepPercent ?? 0)
167
173
  : totalSteps >= 2
168
- ? otaStatus?.overallPercent ?? 0
169
- : otaStatus?.stepPercent ?? 0
174
+ ? (otaStatus?.overallPercent ?? 0)
175
+ : (otaStatus?.stepPercent ?? 0)
170
176
  return Math.min(Math.max(rawPercent, snapshot.mtkInstallStallSimulatedPercent ?? 0, 0), 100)
171
177
  }
172
178
 
@@ -215,7 +221,9 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
215
221
  const [completedReleaseTransition, setCompletedReleaseTransition] = useState<MentraLiveOtaReleaseTransition | null>(
216
222
  null,
217
223
  )
224
+ const [completedUpdate, setCompletedUpdate] = useState(false)
218
225
  const [completedChangelogs, setCompletedChangelogs] = useState<ReleaseChangelog[]>([])
226
+ const [batteryBlocked, setBatteryBlocked] = useState(false)
219
227
  const [checkGeneration, setCheckGeneration] = useState(0)
220
228
  const performCheckGenerationRef = useRef(0)
221
229
  const activeCheckKeyRef = useRef<string | null>(null)
@@ -249,6 +257,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
249
257
  const returnToCheck = useCallback(() => {
250
258
  installActionPendingRef.current = false
251
259
  autoChainAdvancedRef.current = false
260
+ setCheckState("checking")
252
261
  setPage("check")
253
262
  setCheckGeneration((generation) => generation + 1)
254
263
  }, [])
@@ -259,6 +268,28 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
259
268
  setPage("progress")
260
269
  }, [])
261
270
 
271
+ const continueApprovedChain = useCallback(
272
+ (result: OtaCheckCurrentGlassesResult): boolean => {
273
+ if (installActionPendingRef.current || !isOtaAutoChainActive() || !result.updateInfo) return false
274
+ const snapshot = ota.snapshot()
275
+ if (!snapshot.wifiStatusKnown || (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1)) return false
276
+
277
+ const admission = tryAdvanceOtaAutoChain(
278
+ otaAutoChainFingerprint(result),
279
+ result.updateInfo.isDowngrade === true,
280
+ releaseRangeTargetVersion(result),
281
+ result.releaseVersion,
282
+ )
283
+ if (!admission.advance) return false
284
+
285
+ checkCompletedRef.current = true
286
+ ota.installSession.prepare(result)
287
+ navigateToProgress()
288
+ return true
289
+ },
290
+ [navigateToProgress],
291
+ )
292
+
262
293
  useEffect(() => {
263
294
  if (!runtimeReady || page !== "check") return
264
295
  const MIN_DISPLAY_TIME_MS = 1100
@@ -279,18 +310,18 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
279
310
 
280
311
  const performCheck = async () => {
281
312
  if (checkCompletedRef.current) return
313
+ if (isOtaAutoChainActive() && !otaSnapshot.ready) {
314
+ const remainingMs = otaAutoChainReconnectWaitRemaining()
315
+ if (remainingMs === null) return
316
+ reconnectTimeout = setTimeout(() => {
317
+ if (cancelled || myGeneration !== performCheckGenerationRef.current || ota.snapshot().ready) return
318
+ stopOtaAutoChain()
319
+ checkCompletedRef.current = true
320
+ setCheckState("error")
321
+ }, remainingMs)
322
+ return
323
+ }
282
324
  if (!otaSnapshot.connected) {
283
- if (isOtaAutoChainActive()) {
284
- const remainingMs = otaAutoChainReconnectWaitRemaining()
285
- if (remainingMs === null) return
286
- reconnectTimeout = setTimeout(() => {
287
- if (cancelled || myGeneration !== performCheckGenerationRef.current || ota.snapshot().connected) return
288
- stopOtaAutoChain()
289
- checkCompletedRef.current = true
290
- setCheckState("error")
291
- }, remainingMs)
292
- return
293
- }
294
325
  if (checkStartedRef.current) {
295
326
  stopOtaAutoChain()
296
327
  checkCompletedRef.current = true
@@ -364,34 +395,21 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
364
395
  const fromVersion = currentReleaseVersion(ota.snapshot().appVersion)
365
396
  setOfferedReleaseTransition(result.releaseVersion ? {fromVersion, toVersion: result.releaseVersion} : null)
366
397
  }
367
- if (isOtaAutoChainActive()) {
368
- const snapshot = ota.snapshot()
369
- if (!snapshot.wifiStatusKnown) return
370
- if (!snapshot.wifiConnected && snapshot.hotspotOtaVersion !== 1) {
371
- stopOtaAutoChain()
372
- } else {
373
- const admission = tryAdvanceOtaAutoChain(
374
- fingerprint,
375
- result.updateInfo.isDowngrade === true,
376
- releaseRangeTargetVersion(result),
377
- result.releaseVersion,
378
- )
379
- if (admission.advance) {
380
- checkCompletedRef.current = true
381
- ota.installSession.prepare(result)
382
- navigateToProgress()
383
- return
384
- }
385
- }
398
+ if (isOtaAutoChainActive() && !ota.snapshot().wifiStatusKnown) {
399
+ checkCompletedRef.current = true
400
+ return
386
401
  }
402
+ if (continueApprovedChain(result)) return
387
403
  checkCompletedRef.current = true
388
404
  setCheckState("update_available")
389
405
  return
390
406
  }
391
- if (isOtaAutoChainActive()) {
407
+ const completedApprovedSession = isOtaAutoChainActive()
408
+ if (completedApprovedSession) {
392
409
  setCompletedChangelogs(releaseChangelogsForActiveChain())
393
410
  setCompletedReleaseTransition(releaseTransitionFromRange(otaAutoChainReleaseRange()))
394
411
  }
412
+ setCompletedUpdate(completedApprovedSession)
395
413
  stopOtaAutoChain()
396
414
  checkCompletedRef.current = true
397
415
  ota.clearUpdateAvailable()
@@ -412,7 +430,42 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
412
430
  cancelled = true
413
431
  if (reconnectTimeout) clearTimeout(reconnectTimeout)
414
432
  }
415
- }, [checkGeneration, navigateToProgress, otaSnapshot.connected, otaSnapshot.wifiStatusKnown, page, runtimeReady])
433
+ }, [
434
+ checkGeneration,
435
+ continueApprovedChain,
436
+ otaSnapshot.connected,
437
+ otaSnapshot.ready,
438
+ otaSnapshot.wifiStatusKnown,
439
+ page,
440
+ runtimeReady,
441
+ ])
442
+
443
+ useEffect(() => {
444
+ if (
445
+ !runtimeReady ||
446
+ page !== "check" ||
447
+ (checkState !== "checking" && checkState !== "update_available") ||
448
+ !otaSnapshot.wifiStatusKnown ||
449
+ !isOtaAutoChainActive()
450
+ ) {
451
+ return
452
+ }
453
+ const result = selectedCheckResultRef.current
454
+ if (!result?.updateAvailable || !result.updateInfo) return
455
+ if (!otaSnapshot.wifiConnected && otaSnapshot.hotspotOtaVersion !== 1) {
456
+ setCheckState("update_available")
457
+ return
458
+ }
459
+ continueApprovedChain(result)
460
+ }, [
461
+ checkState,
462
+ continueApprovedChain,
463
+ otaSnapshot.hotspotOtaVersion,
464
+ otaSnapshot.wifiConnected,
465
+ otaSnapshot.wifiStatusKnown,
466
+ page,
467
+ runtimeReady,
468
+ ])
416
469
 
417
470
  useEffect(() => {
418
471
  if (!runtimeReady || page !== "progress") return
@@ -457,8 +510,10 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
457
510
  }, [installSnapshot.connected, installSnapshot.displayState, page, returnToCheck])
458
511
 
459
512
  const check = useCallback(() => {
513
+ setBatteryBlocked(false)
460
514
  setOfferedReleaseTransition(null)
461
515
  setCompletedReleaseTransition(null)
516
+ setCompletedUpdate(false)
462
517
  setCompletedChangelogs([])
463
518
  setPage("check")
464
519
  setCheckState("checking")
@@ -474,6 +529,10 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
474
529
  return
475
530
  }
476
531
  const snapshot = ota.snapshot()
532
+ if (snapshot.batteryLevel !== null && snapshot.batteryLevel < MINIMUM_OTA_BATTERY_LEVEL) {
533
+ setBatteryBlocked(true)
534
+ return
535
+ }
477
536
  if (!snapshot.wifiStatusKnown) {
478
537
  check()
479
538
  return
@@ -483,6 +542,8 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
483
542
  return
484
543
  }
485
544
  ota.installSession.prepare(result)
545
+ setBatteryBlocked(false)
546
+ setCompletedUpdate(false)
486
547
  setCompletedChangelogs([])
487
548
  if (updateFingerprint) {
488
549
  const fromVersion = offeredReleaseTransition
@@ -497,6 +558,15 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
497
558
  navigateToProgress()
498
559
  }, [check, isVersionChange, navigateToProgress, offeredReleaseTransition, page, updateFingerprint])
499
560
 
561
+ useEffect(() => {
562
+ if (
563
+ batteryBlocked &&
564
+ (otaSnapshot.batteryLevel === null || otaSnapshot.batteryLevel >= MINIMUM_OTA_BATTERY_LEVEL)
565
+ ) {
566
+ setBatteryBlocked(false)
567
+ }
568
+ }, [batteryBlocked, otaSnapshot.batteryLevel])
569
+
500
570
  const retryInstall = useCallback(() => {
501
571
  if (page !== "progress") return
502
572
  onFirmwareRestartingChangeRef.current?.(false, true)
@@ -508,7 +578,11 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
508
578
  onFinishedRef.current?.()
509
579
  return
510
580
  }
511
- if (installSnapshot.displayState === "restarting") stopOtaAutoChain()
581
+ const restartStillInProgress =
582
+ installSnapshot.displayState === "restarting" ||
583
+ installSnapshot.versionChangePhase === "restarting" ||
584
+ installSnapshot.versionChangePhase === "verifying"
585
+ if (restartStillInProgress) return
512
586
  const requiresGlassesReboot = shouldRequireGlassesRebootForBesFailure(
513
587
  installSnapshot.otaStatus,
514
588
  installSnapshot.otaProgress,
@@ -534,10 +608,12 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
534
608
  const state = useMemo<MentraLiveOtaState>(() => {
535
609
  const hotspotSupported = otaSnapshot.hotspotOtaVersion === 1
536
610
  const canInstall = otaSnapshot.wifiStatusKnown && (otaSnapshot.wifiConnected || hotspotSupported)
611
+ const autoChainActive = isOtaAutoChainActive()
537
612
  if (!runtimeReady) {
538
613
  return {
539
614
  screen: "initializing",
540
615
  connected: otaSnapshot.connected,
616
+ batteryLevel: otaSnapshot.batteryLevel,
541
617
  transport: null,
542
618
  updateRequired: isUpdateRequired,
543
619
  versionChange: isVersionChange,
@@ -563,6 +639,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
563
639
  canDiscard: false,
564
640
  canOpenWifiSetup: false,
565
641
  continueDisabled: false,
642
+ completedUpdate: false,
566
643
  releaseTransition: null,
567
644
  changelogs: [],
568
645
  }
@@ -570,29 +647,27 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
570
647
 
571
648
  if (page === "check") {
572
649
  const wifiRequired = otaSnapshot.wifiStatusKnown && !otaSnapshot.wifiConnected && !hotspotSupported
573
- const screen: MentraLiveOtaScreen =
574
- checkState === "checking"
575
- ? "checking"
576
- : checkState === "update_available"
577
- ? wifiRequired
578
- ? "wifi_required"
579
- : "update_available"
580
- : checkState === "no_update"
581
- ? "up_to_date"
582
- : checkState === "dev_build"
583
- ? "dev_build"
584
- : errorKind === "pin_unavailable"
585
- ? "update_info_unavailable"
586
- : "check_failed"
587
- const error: MentraLiveOtaError | null =
588
- screen === "check_failed"
589
- ? {code: "check_failed", message: "Couldn't check for updates. Please check your connection and try again."}
590
- : screen === "update_info_unavailable"
591
- ? {code: "update_info_unavailable", message: "Update information for this app version is unavailable."}
592
- : null
650
+ let screen: MentraLiveOtaScreen
651
+ if (checkState === "checking") screen = autoChainActive ? "finishing" : "checking"
652
+ else if (checkState === "update_available") {
653
+ screen = wifiRequired ? "wifi_required" : batteryBlocked ? "battery_required" : "update_available"
654
+ } else if (checkState === "no_update") screen = "up_to_date"
655
+ else if (checkState === "dev_build") screen = "dev_build"
656
+ else screen = errorKind === "pin_unavailable" ? "update_info_unavailable" : "check_failed"
657
+
658
+ let error: MentraLiveOtaError | null = null
659
+ if (screen === "check_failed") {
660
+ error = {
661
+ code: "check_failed",
662
+ message: "Couldn't check for updates. Please check your connection and try again.",
663
+ }
664
+ } else if (screen === "update_info_unavailable") {
665
+ error = {code: "update_info_unavailable", message: "Update information for this app version is unavailable."}
666
+ }
593
667
  return {
594
668
  screen,
595
669
  connected: otaSnapshot.connected,
670
+ batteryLevel: otaSnapshot.batteryLevel,
596
671
  transport: otaSnapshot.wifiConnected ? "wifi" : hotspotSupported ? "hotspot" : null,
597
672
  updateRequired: isUpdateRequired,
598
673
  versionChange: isVersionChange,
@@ -614,13 +689,19 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
614
689
  canInstall: screen === "update_available" && canInstall,
615
690
  canRetry: screen === "check_failed",
616
691
  canFinish: screen === "up_to_date" || screen === "dev_build" || screen === "update_info_unavailable",
617
- canDismiss: (screen === "update_available" || screen === "wifi_required") && !isUpdateRequired,
692
+ canDismiss:
693
+ (screen === "update_available" || screen === "wifi_required" || screen === "battery_required") &&
694
+ !isUpdateRequired &&
695
+ !autoChainActive,
618
696
  canDiscard: false,
619
697
  canOpenWifiSetup: screen === "wifi_required",
620
698
  continueDisabled: false,
699
+ completedUpdate: screen === "up_to_date" && completedUpdate,
621
700
  releaseTransition:
622
701
  screen === "update_available" || screen === "wifi_required"
623
- ? offeredReleaseTransition
702
+ ? autoChainActive
703
+ ? releaseTransitionFromRange(otaAutoChainReleaseRange())
704
+ : offeredReleaseTransition
624
705
  : screen === "up_to_date"
625
706
  ? completedReleaseTransition
626
707
  : null,
@@ -641,7 +722,8 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
641
722
  const displayedError = requiresGlassesReboot
642
723
  ? BES_INSTALL_RESTART_MESSAGE
643
724
  : installSnapshot.errorMsg || getOtaErrorMessage(installSnapshot.otaStatus?.error)
644
- const screen = progressScreen(installSnapshot)
725
+ const progressState = progressScreen(installSnapshot)
726
+ const screen = progressState === "complete" && autoChainActive ? "finishing" : progressState
645
727
  const error =
646
728
  screen === "failed"
647
729
  ? {
@@ -654,6 +736,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
654
736
  return {
655
737
  screen,
656
738
  connected: installSnapshot.connected,
739
+ batteryLevel: otaSnapshot.batteryLevel,
657
740
  transport: installSnapshot.transport,
658
741
  updateRequired: isUpdateRequired,
659
742
  versionChange: installSnapshot.isVersionChange,
@@ -677,18 +760,21 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
677
760
  error,
678
761
  canInstall: false,
679
762
  canRetry: screen === "failed" && !requiresGlassesReboot,
680
- canFinish: screen === "complete" || screen === "restarting" || (screen === "failed" && requiresGlassesReboot),
763
+ canFinish: screen === "complete" || (screen === "failed" && requiresGlassesReboot),
681
764
  canDismiss: false,
682
765
  canDiscard: screen === "disconnected",
683
766
  canOpenWifiSetup: screen === "failed" && showChangeWifi,
684
767
  continueDisabled: installSnapshot.continueButtonDisabled,
768
+ completedUpdate: false,
685
769
  releaseTransition: releaseTransitionFromRange(otaAutoChainReleaseRange()),
686
770
  changelogs,
687
771
  }
688
772
  }, [
689
773
  checkState,
774
+ batteryBlocked,
690
775
  completedChangelogs,
691
776
  completedReleaseTransition,
777
+ completedUpdate,
692
778
  errorKind,
693
779
  firmwareRestarting,
694
780
  installSnapshot,