From d6651ccd82eafd79ee1f70f5979cb2e17442e5e7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Oct 2025 23:09:55 -0700 Subject: [PATCH] raylib: implement developer panel (#36292) * first pass by cursor * fix * tell it what's good * stash * desc * clean up junk * alpha long can't use onroad cycle again due to faults * lint * fix kb --- selfdrive/ui/layouts/settings/developer.py | 142 ++++++++++++++---- selfdrive/ui/layouts/settings/toggles.py | 5 +- .../ui/tests/test_ui/raylib_screenshots.py | 2 +- 3 files changed, 115 insertions(+), 34 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index cfef0f84d1..143bb2c262 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -1,5 +1,6 @@ from openpilot.common.params import Params from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.list_view import toggle_item from openpilot.system.ui.widgets.scroller import Scroller @@ -10,11 +11,15 @@ DESCRIPTIONS = { "ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " + "See https://docs.comma.ai/how-to/connect-to-comma for more info." ), - 'joystick_debug_mode': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", 'ssh_key': ( "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " + "other than your own. A comma employee will NEVER ask you to add their GitHub username." ), + 'alpha_longitudinal': ( + "WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).

" + + "On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " + + "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha." + ), } @@ -22,32 +27,55 @@ class DeveloperLayout(Widget): def __init__(self): super().__init__() self._params = Params() + self._is_release = self._params.get_bool("IsReleaseBranch") + + # Build items and keep references for callbacks/state updates + self._adb_toggle = toggle_item( + "Enable ADB", + description=DESCRIPTIONS["enable_adb"], + initial_state=self._params.get_bool("AdbEnabled"), + callback=self._on_enable_adb, + ) + + # SSH enable toggle + SSH key management + self._ssh_toggle = toggle_item( + "Enable SSH", + description="", + initial_state=self._params.get_bool("SshEnabled"), + callback=self._on_enable_ssh, + ) + self._ssh_keys = ssh_key_item("SSH Keys", description=DESCRIPTIONS["ssh_key"]) + + self._joystick_toggle = toggle_item( + "Joystick Debug Mode", + description="", + initial_state=self._params.get_bool("JoystickDebugMode"), + callback=self._on_joystick_debug_mode, + ) + + self._long_maneuver_toggle = toggle_item( + "Longitudinal Maneuver Mode", + description="", + initial_state=self._params.get_bool("LongitudinalManeuverMode"), + callback=self._on_long_maneuver_mode, + ) + + self._alpha_long_toggle = toggle_item( + "openpilot Longitudinal Control (Alpha)", + description=DESCRIPTIONS["alpha_longitudinal"], + initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), + callback=self._on_alpha_long_enabled, + ) + + self._alpha_long_toggle.set_description(self._alpha_long_toggle.description + " Changing this setting will restart openpilot if the car is powered on.") + items = [ - toggle_item( - "Enable ADB", - description=DESCRIPTIONS["enable_adb"], - initial_state=self._params.get_bool("AdbEnabled"), - callback=self._on_enable_adb, - ), - ssh_key_item("SSH Key", description=DESCRIPTIONS["ssh_key"]), - toggle_item( - "Joystick Debug Mode", - description=DESCRIPTIONS["joystick_debug_mode"], - initial_state=self._params.get_bool("JoystickDebugMode"), - callback=self._on_joystick_debug_mode, - ), - toggle_item( - "Longitudinal Maneuver Mode", - description="", - initial_state=self._params.get_bool("LongitudinalManeuverMode"), - callback=self._on_long_maneuver_mode, - ), - toggle_item( - "openpilot Longitudinal Control (Alpha)", - description="", - initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), - callback=self._on_alpha_long_enabled, - ), + self._adb_toggle, + self._ssh_toggle, + self._ssh_keys, + self._joystick_toggle, + self._long_maneuver_toggle, + self._alpha_long_toggle, ] self._scroller = Scroller(items, line_separator=True, spacing=0) @@ -55,7 +83,61 @@ class DeveloperLayout(Widget): def _render(self, rect): self._scroller.render(rect) - def _on_enable_adb(self): pass - def _on_joystick_debug_mode(self): pass - def _on_long_maneuver_mode(self): pass - def _on_alpha_long_enabled(self): pass + def show_event(self): + self._update_toggles() + + def _update_toggles(self): + # Hide non-release toggles on release builds + for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): + item.set_visible(not self._is_release) + + # CP gating + if ui_state.CP is not None: + alpha_avail = ui_state.CP.alphaLongitudinalAvailable + if not alpha_avail or self._is_release: + self._alpha_long_toggle.set_visible(False) + self._params.remove("AlphaLongitudinalEnabled") + else: + self._alpha_long_toggle.set_visible(True) + + self._long_maneuver_toggle.action_item.set_enabled(ui_state.has_longitudinal_control and ui_state.is_offroad) + else: + self._long_maneuver_toggle.action_item.set_enabled(False) + self._alpha_long_toggle.set_visible(False) + + # TODO: make a param control list item so we don't need to manage internal state as much here + # refresh toggles from params to mirror external changes + for key, item in ( + ("AdbEnabled", self._adb_toggle), + ("SshEnabled", self._ssh_toggle), + ("JoystickDebugMode", self._joystick_toggle), + ("LongitudinalManeuverMode", self._long_maneuver_toggle), + ("AlphaLongitudinalEnabled", self._alpha_long_toggle), + ): + item.action_item.set_state(self._params.get_bool(key)) + + def _update_state(self): + # Disable toggles that require onroad restart + # TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault + for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): + item.action_item.set_enabled(ui_state.is_offroad) + + def _on_enable_adb(self, state: bool): + self._params.put_bool("AdbEnabled", state) + + def _on_enable_ssh(self, state: bool): + self._params.put_bool("SshEnabled", state) + + def _on_joystick_debug_mode(self, state: bool): + self._params.put_bool("JoystickDebugMode", state) + self._params.put_bool("LongitudinalManeuverMode", False) + self._long_maneuver_toggle.action_item.set_state(False) + + def _on_long_maneuver_mode(self, state: bool): + self._params.put_bool("LongitudinalManeuverMode", state) + self._params.put_bool("JoystickDebugMode", False) + self._joystick_toggle.action_item.set_state(False) + + def _on_alpha_long_enabled(self, state: bool): + self._params.put_bool("AlphaLongitudinalEnabled", state) + self._update_toggles() diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 01f663d4b9..fddcf32fbd 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -34,6 +34,7 @@ class TogglesLayout(Widget): def __init__(self): super().__init__() self._params = Params() + self._is_release = self._params.get_bool("IsReleaseBranch") # param, title, desc, icon, needs_restart self._toggle_defs = { @@ -158,8 +159,6 @@ class TogglesLayout(Widget): "The Experimental mode logo will also be shown in the top right corner." ) - is_release = self._params.get_bool("IsReleaseBranch") - if ui_state.CP is not None: if ui_state.has_longitudinal_control: self._toggles["ExperimentalMode"].action_item.set_enabled(True) @@ -176,7 +175,7 @@ class TogglesLayout(Widget): long_desc = unavailable + " openpilot longitudinal control may come in a future update." if ui_state.CP.getAlphaLongitudinalAvailable(): - if is_release: + if self._is_release: long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " + "Experimental mode, on non-release branches.") else: diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index e77a2d4d7e..fb42b94d6d 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -76,7 +76,7 @@ def setup_settings_developer(click, pm: PubMaster): def setup_keyboard(click, pm: PubMaster): setup_settings_developer(click, pm) - click(1930, 270) + click(1930, 470) def setup_pair_device(click, pm: PubMaster):