ui: Implement core device settings functionality with enhanced dynamic controls (#35507)

* implement device settings functionality with power controls

* format

* Update selfdrive/ui/layouts/settings/device.py

Co-authored-by: Shane Smiskol <shane@smiskol.com>

* Update selfdrive/ui/layouts/settings/device.py

Co-authored-by: Shane Smiskol <shane@smiskol.com>

* add comment back

* add comments back

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
This commit is contained in:
Dean Lee
2025-06-10 05:45:29 +08:00
committed by GitHub
parent d3b300a148
commit 61b8f6f478
3 changed files with 178 additions and 56 deletions
+55 -19
View File
@@ -7,7 +7,8 @@ from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialo
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.hardware import TICI
from openpilot.system.ui.lib.application import gui_app, Widget, DialogResult
from openpilot.system.ui.lib.list_view import ListView, text_item, button_item
from openpilot.system.ui.lib.list_view import ListView, text_item, button_item, dual_button_item
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog, alert_dialog
@@ -28,55 +29,58 @@ class DeviceLayout(Widget):
super().__init__()
self._params = Params()
self._select_language_dialog: MultiOptionDialog | None = None
self._driver_camera: DriverCameraDialog | None = None
self._pair_device_dialog: PairingDialog | None = None
items = self._initialize_items()
self._list_widget = ListView(items)
def _initialize_items(self):
dongle_id = self._params.get("DongleId", encoding="utf-8") or "N/A"
serial = self._params.get("HardwareSerial") or "N/A"
items = [
text_item("Dongle ID", dongle_id),
text_item("Serial", serial),
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], self._on_pair_device),
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._on_driver_camera),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._on_reset_calibration),
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device),
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt),
button_item("Regulatory", "VIEW", callback=self._on_regulatory, visible=TICI),
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide),
button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt),
]
if TICI:
items.append(button_item("Regulatory", "VIEW", callback=self._on_regulatory))
items.append(button_item("Change Language", "CHANGE", callback=self._on_change_language))
self._list_widget = ListView(items)
self._select_language_dialog: MultiOptionDialog | None = None
self._driver_camera: DriverCameraDialog | None = None
return items
def _render(self, rect):
self._list_widget.render(rect)
def _on_change_language(self):
def _show_language_selection(self):
try:
languages_file = os.path.join(BASEDIR, "selfdrive/ui/translations/languages.json")
with open(languages_file, encoding='utf-8') as f:
languages = json.load(f)
self._select_language_dialog = MultiOptionDialog("Select a language", languages)
gui_app.set_modal_overlay(self._select_language_dialog, callback=self._on_select_lang_dialog_closed)
gui_app.set_modal_overlay(self._select_language_dialog, callback=self._handle_language_selection)
except FileNotFoundError:
pass
def _on_select_lang_dialog_closed(self, result: int):
def _handle_language_selection(self, result: int):
if result == 1 and self._select_language_dialog:
selected_language = self._select_language_dialog.selection
self._params.put("LanguageSetting", selected_language)
self._select_language_dialog = None
def _on_driver_camera(self):
def _show_driver_camera(self):
if not self._driver_camera:
self._driver_camera = DriverCameraDialog()
gui_app.set_modal_overlay(self._driver_camera, callback=lambda result: setattr(self, '_driver_camera', None))
def _on_reset_calibration(self):
def _reset_calibration_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reset Calibration"))
return
@@ -97,6 +101,38 @@ class DeviceLayout(Widget):
self._params.remove("LiveDelay")
self._params.put_bool("OnroadCycleRequested", True)
def _on_pair_device(self): pass
def _reboot_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reboot"))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to reboot?", "Reboot"),
callback=self._perform_reboot,
)
def _perform_reboot(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
self._params.put_bool_nonblocking("DoReboot", True)
def _power_off_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Power Off"))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to power off?", "Power Off"),
callback=self._perform_power_off,
)
def _perform_power_off(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
self._params.put_bool_nonblocking("DoShutdown", True)
def _pair_device(self):
if not self._pair_device_dialog:
self._pair_device_dialog = PairingDialog()
gui_app.set_modal_overlay(self._pair_device_dialog, callback=lambda result: setattr(self, '_pair_device_dialog', None))
def _on_review_training_guide(self): pass
def _on_regulatory(self): pass
+6
View File
@@ -66,6 +66,12 @@ class UIState:
def engaged(self) -> bool:
return self.started and self.sm["selfdriveState"].enabled
def is_onroad(self) -> bool:
return self.started
def is_offroad(self) -> bool:
return not self.started
def update(self) -> None:
self.sm.update(0)
self._update_state()