54e6124925
version: sunnypilot v2026.001.000 (dev) date: 2026-05-07T23:07:19 master commit: c28eb958740187620f2282023b8f1997cf90f583
222 lines
8.4 KiB
Python
222 lines
8.4 KiB
Python
"""
|
|
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.
|
|
"""
|
|
from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout
|
|
from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog
|
|
from openpilot.selfdrive.ui.ui_state import ui_state
|
|
from openpilot.system.hardware import HARDWARE
|
|
from openpilot.system.ui.lib.application import gui_app
|
|
from openpilot.system.ui.lib.multilang import tr
|
|
from openpilot.system.ui.sunnypilot.widgets.list_view import option_item_sp, multiple_button_item_sp, button_item_sp, \
|
|
dual_button_item_sp, Spacer
|
|
from openpilot.system.ui.widgets import DialogResult
|
|
from openpilot.system.ui.widgets.button import ButtonStyle
|
|
from openpilot.system.ui.widgets.confirm_dialog import alert_dialog, ConfirmDialog
|
|
from openpilot.system.ui.widgets.list_view import text_item
|
|
from openpilot.system.ui.widgets.scroller_tici import LineSeparator
|
|
|
|
offroad_time_options = {
|
|
0: 0,
|
|
1: 5,
|
|
2: 10,
|
|
3: 15,
|
|
4: 30,
|
|
5: 60,
|
|
6: 120,
|
|
7: 180,
|
|
8: 300,
|
|
9: 600,
|
|
10: 1440,
|
|
11: 1800,
|
|
}
|
|
|
|
|
|
class DeviceLayoutSP(DeviceLayout):
|
|
def __init__(self):
|
|
DeviceLayout.__init__(self)
|
|
self._scroller._line_separator = None
|
|
|
|
def _initialize_items(self):
|
|
DeviceLayout._initialize_items(self)
|
|
|
|
# Using dual button with no right button for better alignment
|
|
self._always_offroad_btn = dual_button_item_sp(
|
|
left_text=lambda: tr("Enable Always Offroad"),
|
|
left_callback=self._handle_always_offroad,
|
|
right_text="",
|
|
right_callback=None,
|
|
)
|
|
self._always_offroad_btn.action_item.right_button.set_visible(False)
|
|
|
|
self._max_time_offroad = option_item_sp(
|
|
title=lambda: tr("Max Time Offroad"),
|
|
description=lambda: tr("Device will automatically shutdown after set time once the engine is turned off.\n(30h is the default)"),
|
|
param="MaxTimeOffroad",
|
|
min_value=0,
|
|
max_value=11,
|
|
value_change_step=1,
|
|
on_value_changed=None,
|
|
enabled=True,
|
|
icon="",
|
|
value_map=offroad_time_options,
|
|
label_width=360,
|
|
use_float_scaling=False,
|
|
inline=True,
|
|
label_callback=self._update_max_time_offroad_label
|
|
)
|
|
|
|
self._device_wake_mode = multiple_button_item_sp(
|
|
title=lambda: tr("Wake Up Behavior"),
|
|
description=self.wake_mode_description,
|
|
param="DeviceBootMode",
|
|
buttons=[lambda: tr("Default"), lambda: tr("Offroad")],
|
|
button_width=364,
|
|
callback=None,
|
|
inline=True,
|
|
)
|
|
|
|
self._quiet_mode_and_dcam = dual_button_item_sp(
|
|
left_text=lambda: tr("Quiet Mode"),
|
|
right_text=lambda: tr("Driver Camera Preview"),
|
|
left_callback=lambda: ui_state.params.put_bool("QuietMode", not ui_state.params.get_bool("QuietMode")),
|
|
right_callback=lambda: gui_app.push_widget(DriverCameraDialog())
|
|
)
|
|
self._quiet_mode_and_dcam.action_item.right_button.set_button_style(ButtonStyle.NORMAL)
|
|
|
|
self._reg_and_training = dual_button_item_sp(
|
|
left_text=lambda: tr("Regulatory"),
|
|
left_callback=self._on_regulatory,
|
|
right_text=lambda: tr("Training Guide"),
|
|
right_callback=self._on_review_training_guide
|
|
)
|
|
self._reg_and_training.action_item.right_button.set_button_style(ButtonStyle.NORMAL)
|
|
|
|
self._onroad_uploads_and_reset_settings = dual_button_item_sp(
|
|
left_text=lambda: tr("Onroad Uploads"),
|
|
left_callback=lambda: ui_state.params.put_bool("OnroadUploads", not ui_state.params.get_bool("OnroadUploads")),
|
|
right_text=lambda: tr("Reset Settings"),
|
|
right_callback=self._reset_settings
|
|
)
|
|
|
|
self._power_buttons = dual_button_item_sp(
|
|
left_text=lambda: tr("Reboot"),
|
|
right_text=lambda: tr("Power Off"),
|
|
left_callback=self._reboot_prompt,
|
|
right_callback=self._power_off_prompt
|
|
)
|
|
|
|
items = [
|
|
text_item(lambda: tr("Dongle ID"), self._params.get("DongleId") or (lambda: tr("N/A"))),
|
|
LineSeparator(),
|
|
text_item(lambda: tr("Serial"), self._params.get("HardwareSerial") or (lambda: tr("N/A"))),
|
|
LineSeparator(),
|
|
self._pair_device_btn,
|
|
LineSeparator(),
|
|
self._reset_calib_btn,
|
|
LineSeparator(),
|
|
button_item_sp(lambda: tr("Change Language"), lambda: tr("CHANGE"), callback=self._show_language_dialog),
|
|
LineSeparator(),
|
|
self._device_wake_mode,
|
|
LineSeparator(),
|
|
self._max_time_offroad,
|
|
LineSeparator(height=10),
|
|
self._quiet_mode_and_dcam,
|
|
self._reg_and_training,
|
|
self._onroad_uploads_and_reset_settings,
|
|
Spacer(10),
|
|
LineSeparator(height=10),
|
|
self._power_buttons,
|
|
]
|
|
|
|
return items
|
|
|
|
def _offroad_transition(self):
|
|
self._power_buttons.action_item.right_button.set_visible(ui_state.is_offroad())
|
|
|
|
@staticmethod
|
|
def wake_mode_description() -> str:
|
|
def_str = tr("Default: Device will boot/wake-up normally & will be ready to engage.")
|
|
offrd_str = tr("Offroad: Device will be in Always Offroad mode after boot/wake-up.")
|
|
header = tr("Controls state of the device after boot/sleep.")
|
|
|
|
return f"{header}\n\n{def_str}\n{offrd_str}"
|
|
|
|
@staticmethod
|
|
def _reset_settings():
|
|
def _do_reset(result: int):
|
|
if result == DialogResult.CONFIRM:
|
|
for _key in ui_state.params.all_keys():
|
|
ui_state.params.remove(_key)
|
|
HARDWARE.reboot()
|
|
|
|
def _second_confirm(result: int):
|
|
if result == DialogResult.CONFIRM:
|
|
gui_app.push_widget(ConfirmDialog(
|
|
text=tr("The reset cannot be undone. You have been warned."),
|
|
confirm_text=tr("Confirm"), callback=_do_reset
|
|
))
|
|
|
|
gui_app.push_widget(ConfirmDialog(
|
|
text=tr("Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back."),
|
|
confirm_text=tr("Reset"), callback=_second_confirm
|
|
))
|
|
|
|
@staticmethod
|
|
def _handle_always_offroad():
|
|
if ui_state.engaged:
|
|
gui_app.push_widget(alert_dialog(tr("Disengage to Enter Always Offroad Mode")))
|
|
return
|
|
|
|
_offroad_mode_state = ui_state.params.get_bool("OffroadMode")
|
|
_offroad_mode_str = tr("Are you sure you want to exit Always Offroad mode?") if _offroad_mode_state else \
|
|
tr("Are you sure you want to enter Always Offroad mode?")
|
|
|
|
def _set_always_offroad(result: int):
|
|
if result == DialogResult.CONFIRM and not ui_state.engaged:
|
|
ui_state.params.put_bool("OffroadMode", not _offroad_mode_state)
|
|
|
|
gui_app.push_widget(ConfirmDialog(_offroad_mode_str, tr("Confirm"), callback=lambda result: _set_always_offroad(result)))
|
|
|
|
@staticmethod
|
|
def _update_max_time_offroad_label(value: int) -> str:
|
|
label = tr("Always On") if value == 0 else f"{value}" + tr("m") if value < 60 else f"{value // 60}" + tr("h")
|
|
label += tr(" (Default)") if value == 1800 else ""
|
|
return label
|
|
|
|
def _update_state(self):
|
|
super()._update_state()
|
|
|
|
# Handle Always Offroad button
|
|
always_offroad = ui_state.params.get_bool("OffroadMode")
|
|
|
|
# Text & Color
|
|
offroad_mode_btn_text = tr("Exit Always Offroad") if always_offroad else tr("Enable Always Offroad")
|
|
offroad_mode_btn_style = ButtonStyle.PRIMARY if always_offroad else ButtonStyle.DANGER
|
|
self._always_offroad_btn.action_item.left_button.set_text(offroad_mode_btn_text)
|
|
self._always_offroad_btn.action_item.left_button.set_button_style(offroad_mode_btn_style)
|
|
|
|
# Position
|
|
if self._scroller._items.__contains__(self._always_offroad_btn):
|
|
self._scroller._items.remove(self._always_offroad_btn)
|
|
if ui_state.is_offroad() and not always_offroad:
|
|
self._scroller._items.insert(len(self._scroller._items) - 1, self._always_offroad_btn)
|
|
else:
|
|
self._scroller._items.insert(0, self._always_offroad_btn)
|
|
|
|
# Quiet Mode button
|
|
self._quiet_mode_and_dcam.action_item.left_button.set_button_style(ButtonStyle.PRIMARY if ui_state.params.get_bool("QuietMode") else ButtonStyle.NORMAL)
|
|
|
|
# Onroad Uploads
|
|
self._onroad_uploads_and_reset_settings.action_item.left_button.set_button_style(
|
|
ButtonStyle.PRIMARY if ui_state.params.get_bool("OnroadUploads") else ButtonStyle.NORMAL
|
|
)
|
|
|
|
# Offroad only buttons
|
|
self._quiet_mode_and_dcam.action_item.right_button.set_enabled(ui_state.is_offroad())
|
|
self._reg_and_training.action_item.left_button.set_enabled(ui_state.is_offroad())
|
|
self._reg_and_training.action_item.right_button.set_enabled(ui_state.is_offroad())
|
|
self._onroad_uploads_and_reset_settings.action_item.right_button.set_enabled(ui_state.is_offroad())
|