#!/usr/bin/env python3
import os
import pyray as rl
import sys
import threading
from enum import IntEnum
from openpilot . system . ui . lib . application import gui_app , FontWeight
from openpilot . system . ui . lib . button import gui_button
from openpilot . system . ui . lib . label import gui_label
NVME = " /dev/nvme0n1 "
USERDATA = " /dev/disk/by-partlabel/userdata "
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 :
def __init__ ( self , mode ) :
self . mode = mode
self . reset_state = ResetState . NONE
def do_reset ( self ) :
# Best effort to wipe NVME
os . system ( f " sudo umount { NVME } " )
os . system ( f " yes | sudo mkfs.ext4 { NVME } " )
# 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_reset ) . start ( )
def render ( self , rect : rl . Rectangle ) :
rl . gui_set_font ( gui_app . font ( FontWeight . BOLD ) )
label_rect = rl . Rectangle ( rect . x + 140 , rect . y , rect . width - 280 , rect . height )
gui_label ( label_rect , " System Reset " , 90 )
rl . gui_set_font ( gui_app . font ( FontWeight . NORMAL ) )
label_rect . y + = 150
gui_label ( label_rect , self . get_body_text ( ) , 80 )
button_height = 160
button_spacing = 50
button_top = rect . y + rect . height - button_height
button_width = ( rect . width - button_spacing ) / 2.0
if self . reset_state != ResetState . RESETTING :
if self . mode == ResetMode . RECOVER or self . reset_state == ResetState . FAILED :
if gui_button ( rl . Rectangle ( rect . x , button_top , button_width , button_height ) , " Reboot " ) :
os . system ( " sudo reboot " )
elif self . mode == ResetMode . USER_RESET :
if gui_button ( rl . Rectangle ( rect . x , button_top , button_width , button_height ) , " Cancel " ) :
return False
if self . reset_state != ResetState . FAILED :
if gui_button ( rl . Rectangle ( rect . x + button_width + 50 , button_top , button_width , button_height ) , " Confirm " , rl . Color ( 70 , 91 , 234 , 255 ) ) :
self . confirm ( )
return True
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... \n This 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 " )
reset = Reset ( mode )
if mode == ResetMode . FORMAT :
reset . start_reset ( )
for _ in gui_app . render ( ) :
if not reset . render ( rl . Rectangle ( 45 , 200 , gui_app . width - 90 , gui_app . height - 245 ) ) :
break
if __name__ == " __main__ " :
main ( )