@@ -35,6 +35,11 @@ class BootResult:
3535 services_ready : dict [str , bool ]
3636
3737
38+ @dataclass
39+ class BootProgress :
40+ step : str = "starting"
41+
42+
3843class BootSequence :
3944 def __init__ (
4045 self ,
@@ -49,6 +54,7 @@ def __init__(
4954 scan_qr : Callable [[], SecretsPayload | None ] | None = None ,
5055 recovery_committing_handlers : dict | None = None ,
5156 wifi_manager = None ,
57+ progress : "BootProgress | None" = None ,
5258 ):
5359 self .settings = settings
5460 self .paths = paths
@@ -61,6 +67,7 @@ def __init__(
6167 self .scan_qr = scan_qr
6268 self .recovery_committing_handlers = recovery_committing_handlers
6369 self .wifi_manager = wifi_manager
70+ self .progress = progress or BootProgress ()
6471
6572 def prepare_early_services (self ) -> list [str ]:
6673 placeholder_mode = ModeDecision (production = False , reasons = [], capabilities = {})
@@ -86,22 +93,28 @@ async def run(self) -> BootResult:
8693 facts = BootFacts ()
8794 facts .gpio_forced = self ._read_gpio ()
8895
96+ self .progress .step = "checking_secrets"
8997 await self ._maybe_scan_qr (facts )
9098
99+ self .progress .step = "probing_hardware"
91100 camera_device = resolve_camera_device (self .settings .hardware .camera , self .hal .camera_probe )
92101 await asyncio .to_thread (self ._probe_hardware , facts , camera_device )
93102
103+ self .progress .step = "updating_repos"
94104 await asyncio .to_thread (self ._update_repos , facts )
105+ self .progress .step = "flashing_firmware"
95106 await asyncio .to_thread (self ._run_firmware_targets , facts )
96107
97108 mode = decide_mode (facts )
98109
110+ self .progress .step = "starting_services"
99111 mcu_port = resolve_mcu_port (self .settings .hardware .mcu , self .hal .usb_inventory )
100112 self ._prepare_services (mode , camera_device , mcu_port )
101113
102114 main_services = [s for s in self .settings .services if s .phase != "early" ]
103115 order = topological_order (main_services )
104116 services_ready = await self .supervisor .start_ordered (order , mode .capabilities )
117+ self .progress .step = "done"
105118
106119 with self .db .transaction () as conn :
107120 store .update_boot_history (
@@ -132,6 +145,7 @@ async def _maybe_scan_qr(self, facts: BootFacts) -> None:
132145 required = self .settings .secrets .required
133146 missing = secrets_store .missing_required_secrets (self .paths , required )
134147 if missing and self .scan_qr is not None :
148+ self .progress .step = "waiting_for_qr"
135149 payload = await asyncio .to_thread (self .scan_qr )
136150 if payload is not None :
137151 ingest_payload (self .paths , self .journal , payload )
0 commit comments