sunnypilot v2026.002.000
version: sunnypilot v2026.002.000 (staging) date: 2026-07-21T07:44:02 master commit: 2c334ede443d7391d27575af8c854a095ba702a8
This commit is contained in:
commit
de77c89cf6
3995 files changed
+1260688
No files matched your search
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from enum import IntEnum
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.common.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
|
||||
|
||||
|
||||
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: subprocess.run("sudo reboot", shell=True))
|
||||
|
||||
def _do_erase(self):
|
||||
if PC:
|
||||
return
|
||||
|
||||
# Removing data and formatting
|
||||
rm = subprocess.run("sudo rm -rf /data/*", shell=True).returncode
|
||||
subprocess.run(f"sudo umount {USERDATA}", shell=True)
|
||||
fmt = subprocess.run(f"yes | sudo mkfs.ext4 {USERDATA}", shell=True).returncode
|
||||
|
||||
if rm == 0 or fmt == 0:
|
||||
subprocess.run("sudo reboot", shell=True)
|
||||
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
|
||||
|
||||
gui_app.init_window("System Reset", 20)
|
||||
reset = Reset(mode)
|
||||
|
||||
gui_app.push_widget(reset)
|
||||
|
||||
for _ in gui_app.render():
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user