diff --git a/selfdrive/ui/sunnypilot/layouts/settings/developer.py b/selfdrive/ui/sunnypilot/layouts/settings/developer.py
new file mode 100644
index 0000000000..f3a2433373
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/layouts/settings/developer.py
@@ -0,0 +1,106 @@
+"""
+Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
+"""
+import datetime
+import os
+from pathlib import Path
+
+from openpilot.selfdrive.ui.ui_state import ui_state
+from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
+from openpilot.system.hardware import PC
+from openpilot.system.hardware.hw import Paths
+from openpilot.system.ui.lib.application import gui_app
+from openpilot.system.ui.lib.multilang import tr
+from openpilot.system.ui.widgets import DialogResult
+from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
+from openpilot.system.ui.widgets.list_view import button_item
+
+from openpilot.system.ui.sunnypilot.widgets.html_render import HtmlModalSP
+from openpilot.system.ui.sunnypilot.widgets.list_view import toggle_item_sp
+
+PREBUILT_PATH = os.path.join(Paths.comma_home(), "prebuilt") if PC else "/data/openpilot/prebuilt"
+
+
+class DeveloperLayoutSP(DeveloperLayout):
+ def __init__(self):
+ super().__init__()
+ self.error_log_path = os.path.join(Paths.crash_log_root(), "error.log")
+ self._is_release_branch: bool = self._is_release or ui_state.params.get_bool("IsReleaseSpBranch")
+ self._is_development_branch: bool = ui_state.params.get_bool("IsTestedBranch") or ui_state.params.get_bool("IsDevelopmentBranch")
+ self._initialize_items()
+
+ for item in self.items:
+ self._scroller.add_widget(item)
+
+ def _initialize_items(self):
+ self.show_advanced_controls = toggle_item_sp(tr("Show Advanced Controls"),
+ tr("Toggle visibility of advanced sunnypilot controls.
This only changes the visibility of the toggles; " +
+ "it does not change the actual enabled/disabled state."), param="ShowAdvancedControls")
+
+ self.enable_github_runner_toggle = toggle_item_sp(tr("GitHub Runner Service"), tr("Enables or disables the GitHub runner service."),
+ param="EnableGithubRunner")
+
+ self.enable_copyparty_toggle = toggle_item_sp(tr("copyparty Service"),
+ tr("copyparty is a very capable file server, you can use it to download your routes, view your logs " +
+ "and even make some edits on some files from your browser. " +
+ "Requires you to connect to your comma locally via its IP address."), param="EnableCopyparty")
+
+ self.prebuilt_toggle = toggle_item_sp(tr("Quickboot Mode"), "", param="QuickBootToggle", callback=self._on_prebuilt_toggled)
+
+ self.error_log_btn = button_item(tr("Error Log"), tr("VIEW"), tr("View the error log for sunnypilot crashes."), callback=self._on_error_log_clicked)
+
+ self.items: list = [self.show_advanced_controls, self.enable_github_runner_toggle, self.enable_copyparty_toggle, self.prebuilt_toggle, self.error_log_btn,]
+
+ @staticmethod
+ def _on_prebuilt_toggled(state):
+ if state:
+ Path(PREBUILT_PATH).touch(exist_ok=True)
+ else:
+ os.remove(PREBUILT_PATH)
+ ui_state.params.put_bool("QuickBootToggle", state)
+
+ def _on_delete_confirm(self, result):
+ if result == DialogResult.CONFIRM:
+ if os.path.exists(self.error_log_path):
+ os.remove(self.error_log_path)
+
+ def _on_error_log_closed(self, result, log_exists):
+ if result == DialogResult.CONFIRM and log_exists:
+ dialog2 = ConfirmDialog(tr("Would you like to delete this log?"), tr("Yes"), tr("No"), rich=False)
+ gui_app.set_modal_overlay(dialog2, callback=self._on_delete_confirm)
+
+ def _on_error_log_clicked(self):
+ text = ""
+ if os.path.exists(self.error_log_path):
+ text = f"{datetime.datetime.fromtimestamp(os.path.getmtime(self.error_log_path)).strftime('%d-%b-%Y %H:%M:%S').upper()}
"
+ try:
+ with open(self.error_log_path) as file:
+ text += file.read()
+ except Exception:
+ pass
+ dialog = HtmlModalSP(text=text, callback=lambda result: self._on_error_log_closed(result, os.path.exists(self.error_log_path)))
+ gui_app.set_modal_overlay(dialog)
+
+ def _update_state(self):
+ disable_updates = ui_state.params.get_bool("DisableUpdates")
+ show_advanced = ui_state.params.get_bool("ShowAdvancedControls")
+
+ if (prebuilt_file := os.path.exists(PREBUILT_PATH)) != ui_state.params.get_bool("QuickBootToggle"):
+ ui_state.params.put_bool("QuickBootToggle", prebuilt_file)
+ self.prebuilt_toggle.action_item.set_state(prebuilt_file)
+
+ self.prebuilt_toggle.set_visible(show_advanced and not (self._is_release_branch or self._is_development_branch))
+ self.prebuilt_toggle.action_item.set_enabled(disable_updates)
+
+ if disable_updates:
+ self.prebuilt_toggle.set_description(tr("When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it " +
+ "removes the prebuilt file so compilation of locally edited cpp files can be made."))
+ else:
+ self.prebuilt_toggle.set_description(tr("Quickboot mode requires updates to be disabled.
Enable 'Disable Updates' in the Software panel first."))
+
+ self.enable_copyparty_toggle.set_visible(show_advanced)
+ self.enable_github_runner_toggle.set_visible(show_advanced and not self._is_release_branch)
+ self.error_log_btn.set_visible(not self._is_release_branch)
diff --git a/selfdrive/ui/sunnypilot/layouts/settings/settings.py b/selfdrive/ui/sunnypilot/layouts/settings/settings.py
index 4de2cacafd..103dd99efd 100644
--- a/selfdrive/ui/sunnypilot/layouts/settings/settings.py
+++ b/selfdrive/ui/sunnypilot/layouts/settings/settings.py
@@ -9,7 +9,6 @@ from enum import IntEnum
import pyray as rl
from openpilot.selfdrive.ui.layouts.settings import settings as OP
-from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.device import DeviceLayoutSP
from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.software import SoftwareLayoutSP
@@ -31,6 +30,7 @@ from openpilot.selfdrive.ui.sunnypilot.layouts.settings.steering import Steering
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.cruise import CruiseLayout
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.visuals import VisualsLayout
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import DisplayLayout
+from openpilot.selfdrive.ui.sunnypilot.layouts.settings.developer import DeveloperLayoutSP
# from openpilot.selfdrive.ui.sunnypilot.layouts.settings.navigation import NavigationLayout
@@ -125,7 +125,7 @@ class SettingsLayoutSP(OP.SettingsLayout):
OP.PanelType.TRIPS: PanelInfo(tr_noop("Trips"), TripsLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"),
OP.PanelType.VEHICLE: PanelInfo(tr_noop("Vehicle"), VehicleLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"),
OP.PanelType.FIREHOSE: PanelInfo(tr_noop("Firehose"), FirehoseLayout(), icon="../../sunnypilot/selfdrive/assets/offroad/icon_firehose.png"),
- OP.PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout(), icon="icons/shell.png"),
+ OP.PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayoutSP(), icon="icons/shell.png"),
}
def _draw_sidebar(self, rect: rl.Rectangle):
diff --git a/sunnypilot/sunnylink/params_metadata.json b/sunnypilot/sunnylink/params_metadata.json
index 83ec3c7af8..4cc6b18089 100644
--- a/sunnypilot/sunnylink/params_metadata.json
+++ b/sunnypilot/sunnylink/params_metadata.json
@@ -258,11 +258,11 @@
"description": ""
},
"EnableCopyparty": {
- "title": "Enable Copyparty",
+ "title": "copyparty Service",
"description": ""
},
"EnableGithubRunner": {
- "title": "Enable GitHub Runner",
+ "title": "GitHub Runner Service",
"description": ""
},
"EnableSunnylinkUploader": {