Files
StarPilot/system/ui/tici_reset.py
T
Shane Smiskol cefddf4b9b ui: add navigation stack for tici (#37275)
* initial

* start to support nav stack in settings panels + fix some navwidget bugs

* add deprecation warning and move more to new nav stack

* fix overriding NavWidget enabled and do developer panel

* fix interactive timeout and do main

* more device, not done yet

* minor network fixes

* dcam dialog

* start onboarding

* fix onboarding

* do mici setup

* remove now useless CUSTOM_SOFTWARE

* support big ui with old modal overlay

* reset can be old modal overlay, but updater needs new since it uses wifiui

* flip name truthiness to inspire excitement

* all *should* work, but will do pass later

* clean up main

* clean up settiings

* clean up dialog and developer

* cleanup mici setup some

* rm one more

* fix keyboard

* revert

* might as well but clarify

* fix networkinfopage buttons

* lint

* nice clean up from cursor

* animate background fade with position

* fix device overlays

* cursor fix pt1

cursor fix pt2

* rm print

* capital

* temp fix from cursor for onboarding not freeing space after reviewing training guide

* fix home screen scroller snap not resetting

* stash

* nice gradient on top

* 40

* 20

* no gradient

* return unused returns and always show regulatory btn

* nice!

* revert selfdrive/ui

* let's do tici first

* bring back ui

* not sure why __del__, SetupWidget was never deleted?

* device "done"

* network "done!!"

* toggles "done"

* software "done"

* developer "done"

* fix onboarding

* use new modal for debug windows

* and aug

* setup "done"

* clean up

* updater "done"

* reset "done"

* pop first before callbacks in case callbacks push

* fix cmt

* not needed

* remove two commented functions for mici

* clean up application

* typing

* static

* not sure what this means

* fix big

* more static

* actually great catch

* fix cmt
2026-02-20 02:43:11 -08:00

133 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python3
import os
import sys
import threading
import time
from enum import IntEnum
import pyray as rl
from openpilot.system.hardware import PC
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.button import Button, ButtonStyle
from openpilot.system.ui.widgets.label import gui_label, gui_text_box
USERDATA = "/dev/disk/by-partlabel/userdata"
TIMEOUT = 3*60
class ResetMode(IntEnum):
USER_RESET = 0 # user initiated a factory reset from openpilot
RECOVER = 1 # userdata is corrupt for some reason, give a chance to recover
FORMAT = 2 # finish up a factory reset from a tool that doesn't flash an empty partition to userdata
class ResetState(IntEnum):
NONE = 0
CONFIRM = 1
RESETTING = 2
FAILED = 3
class Reset(Widget):
def __init__(self, mode):
super().__init__()
self._mode = mode
self._previous_reset_state = None
self._reset_state = ResetState.NONE
self._cancel_button = Button("Cancel", gui_app.request_close)
self._confirm_button = Button("Confirm", self._confirm, button_style=ButtonStyle.PRIMARY)
self._reboot_button = Button("Reboot", lambda: os.system("sudo reboot"))
def _do_erase(self):
if PC:
return
# Removing data and formatting
rm = os.system("sudo rm -rf /data/*")
os.system(f"sudo umount {USERDATA}")
fmt = os.system(f"yes | sudo mkfs.ext4 {USERDATA}")
if rm == 0 or fmt == 0:
os.system("sudo reboot")
else:
self._reset_state = ResetState.FAILED
def start_reset(self):
self._reset_state = ResetState.RESETTING
threading.Timer(0.1, self._do_erase).start()
def _update_state(self):
if self._reset_state != self._previous_reset_state:
self._previous_reset_state = self._reset_state
self._timeout_st = time.monotonic()
elif self._reset_state != ResetState.RESETTING and (time.monotonic() - self._timeout_st) > TIMEOUT:
exit(0)
def _render(self, _):
content_rect = rl.Rectangle(45, 200, self._rect.width - 90, self._rect.height - 245)
label_rect = rl.Rectangle(content_rect.x + 140, content_rect.y, content_rect.width - 280, 100 * FONT_SCALE)
gui_label(label_rect, "System Reset", 100, font_weight=FontWeight.BOLD)
text_rect = rl.Rectangle(content_rect.x + 140, content_rect.y + 140, content_rect.width - 280, content_rect.height - 90 - 100 * FONT_SCALE)
gui_text_box(text_rect, self._get_body_text(), 90)
button_height = 160
button_spacing = 50
button_top = content_rect.y + content_rect.height - button_height
button_width = (content_rect.width - button_spacing) / 2.0
if self._reset_state != ResetState.RESETTING:
if self._mode == ResetMode.RECOVER:
self._reboot_button.render(rl.Rectangle(content_rect.x, button_top, button_width, button_height))
elif self._mode == ResetMode.USER_RESET:
self._cancel_button.render(rl.Rectangle(content_rect.x, button_top, button_width, button_height))
if self._reset_state != ResetState.FAILED:
self._confirm_button.render(rl.Rectangle(content_rect.x + button_width + 50, button_top, button_width, button_height))
else:
self._reboot_button.render(rl.Rectangle(content_rect.x, button_top, content_rect.width, button_height))
def _confirm(self):
if self._reset_state == ResetState.CONFIRM:
self.start_reset()
else:
self._reset_state = ResetState.CONFIRM
def _get_body_text(self):
if self._reset_state == ResetState.CONFIRM:
return "Are you sure you want to reset your device?"
if self._reset_state == ResetState.RESETTING:
return "Resetting device...\nThis may take up to a minute."
if self._reset_state == ResetState.FAILED:
return "Reset failed. Reboot to try again."
if self._mode == ResetMode.RECOVER:
return "Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device."
return "System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot."
def main():
mode = ResetMode.USER_RESET
if len(sys.argv) > 1:
if sys.argv[1] == '--recover':
mode = ResetMode.RECOVER
elif sys.argv[1] == "--format":
mode = ResetMode.FORMAT
gui_app.init_window("System Reset", 20, new_modal=True)
reset = Reset(mode)
if mode == ResetMode.FORMAT:
reset.start_reset()
gui_app.push_widget(reset)
for _ in gui_app.render():
pass
if __name__ == "__main__":
main()