diff --git a/selfdrive/assets/icons_mici/settings/device/always_offroad.png b/selfdrive/assets/icons_mici/settings/device/always_offroad.png new file mode 100644 index 0000000000..56f35669c6 --- /dev/null +++ b/selfdrive/assets/icons_mici/settings/device/always_offroad.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e459241d896824f5e8207d568847acab3dedd41caae7af59d4c17e043663b0c9 +size 4035 diff --git a/selfdrive/assets/icons_mici/settings/device/disable_offroad.png b/selfdrive/assets/icons_mici/settings/device/disable_offroad.png new file mode 100644 index 0000000000..146734aafa --- /dev/null +++ b/selfdrive/assets/icons_mici/settings/device/disable_offroad.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27a0fca872d4586f578d246890b83674cdb7ecb03f58b2b0379b4b64a5816053 +size 3908 diff --git a/selfdrive/ui/mici/layouts/main.py b/selfdrive/ui/mici/layouts/main.py index 8f24d0b9b9..f50dd77b01 100644 --- a/selfdrive/ui/mici/layouts/main.py +++ b/selfdrive/ui/mici/layouts/main.py @@ -13,7 +13,7 @@ from openpilot.system.ui.lib.application import gui_app if gui_app.sunnypilot_ui(): from openpilot.selfdrive.ui.sunnypilot.mici.layouts.settings import SettingsLayoutSP as SettingsLayout - + from openpilot.selfdrive.ui.sunnypilot.mici.layouts.home import MiciHomeLayoutSP as MiciHomeLayout ONROAD_DELAY = 2.5 # seconds diff --git a/selfdrive/ui/mici/layouts/settings/settings.py b/selfdrive/ui/mici/layouts/settings/settings.py index a6f59a0389..651f42f460 100644 --- a/selfdrive/ui/mici/layouts/settings/settings.py +++ b/selfdrive/ui/mici/layouts/settings/settings.py @@ -14,6 +14,9 @@ from openpilot.selfdrive.ui.mici.layouts.settings.firehose import FirehoseLayout from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget, NavWidget +if gui_app.sunnypilot_ui(): + from openpilot.selfdrive.ui.sunnypilot.mici.layouts.settings_layouts.device import DeviceLayoutMiciSP as DeviceLayoutMici + class PanelType(IntEnum): TOGGLES = 0 diff --git a/selfdrive/ui/sunnypilot/mici/layouts/home.py b/selfdrive/ui/sunnypilot/mici/layouts/home.py new file mode 100644 index 0000000000..a8d947bcc8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/mici/layouts/home.py @@ -0,0 +1,52 @@ +import time + +from cereal import log +import pyray as rl +from collections.abc import Callable + +from openpilot.selfdrive.ui.mici.layouts.home import MiciHomeLayout +from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.widgets.label import gui_label, MiciLabel, UnifiedLabel +from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR, MousePos +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.system.ui.text import wrap_text +from openpilot.system.version import training_version, RELEASE_BRANCHES + +class MiciHomeLayoutSP(MiciHomeLayout): + def __init__(self): + super().__init__() + + def _render_bottom_status_bar(self): + super()._render_bottom_status_bar() + + ITEM_SPACING = 18 + Y_CENTER = 28 + + if ui_state.always_offroad: + text = "always offroad" + font_size = 28 + padding_x = 10 + padding_y = 10 + font = gui_app.font(FontWeight.BOLD) + + # Measure text size + text_width = measure_text_cached(font, text, font_size).x + text_height = font_size + pos_x = self._rect.x + self.rect.width - ITEM_SPACING - text_width - 5 + + # Calculate rectangle dimensions + rect_width = text_width + padding_x * 2 + rect_height = text_height + padding_y * 2 + + # Draw rounded rectangle background (red) + bg_rect = rl.Rectangle(pos_x, self._rect.y + self.rect.height - rect_height / 2 - Y_CENTER, rect_width, + rect_height) + rl.draw_rectangle_rounded(bg_rect, 0.5, 10, rl.Color(245, 56, 56, 175)) + + # Draw white text centered on the rectangle + text_x = pos_x + padding_x + text_y = self._rect.y + self.rect.height - rect_height / 2 - Y_CENTER + padding_y + rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), font_size, 0, rl.WHITE) + + diff --git a/selfdrive/ui/sunnypilot/mici/layouts/settings.py b/selfdrive/ui/sunnypilot/mici/layouts/settings.py index 69982e2298..5ef7515558 100644 --- a/selfdrive/ui/sunnypilot/mici/layouts/settings.py +++ b/selfdrive/ui/sunnypilot/mici/layouts/settings.py @@ -7,9 +7,12 @@ See the LICENSE.md file in the root directory for more details. from enum import IntEnum from openpilot.selfdrive.ui.mici.layouts.settings import settings as OP -from openpilot.selfdrive.ui.mici.widgets.button import BigButton +from openpilot.selfdrive.ui.mici.widgets.button import BigButton, BigCircleButton +from openpilot.selfdrive.ui.mici.widgets.dialog import BigConfirmationDialogV2 from openpilot.selfdrive.ui.sunnypilot.mici.layouts.sunnylink import SunnylinkLayoutMici from openpilot.selfdrive.ui.sunnypilot.mici.layouts.models import ModelsLayoutMici +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.system.ui.lib.application import gui_app ICON_SIZE = 70 @@ -38,10 +41,30 @@ class SettingsLayoutSP(OP.SettingsLayout): OP.PanelType.MODELS: OP.PanelInfo("models", ModelsLayoutMici(back_callback=lambda: self._set_current_panel(None))), }) + self.disable_always_offroad_btn = BigCircleButton("../../sunnypilot/selfdrive/assets/icons_mici/disable_offroad.png", + red=False, icon_size=(120, 120)) + self.disable_always_offroad_btn.set_click_callback(self._disable_always_offroad) + items = self._scroller._items.copy() + items.insert(0, self.disable_always_offroad_btn) items.insert(1, sunnylink_btn) items.insert(2, models_btn) self._scroller._items.clear() for item in items: self._scroller.add_widget(item) + + def _update_state(self): + super()._update_state() + + # Show "disable always offroad" button only when in always offroad mode + self.disable_always_offroad_btn.set_visible(ui_state.always_offroad) + + def _disable_always_offroad(self): + def _do_disable(): + ui_state.params.put_bool("OffroadMode", False) + self.disable_always_offroad_btn.set_visible(False) + + dlg = BigConfirmationDialogV2(f"slide to exit always offroad", "icons_mici/settings/device/lkas.png", + red=True, confirm_callback=_do_disable) + gui_app.set_modal_overlay(dlg) diff --git a/selfdrive/ui/sunnypilot/mici/layouts/settings_layouts/device.py b/selfdrive/ui/sunnypilot/mici/layouts/settings_layouts/device.py new file mode 100644 index 0000000000..37fead4ef8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/mici/layouts/settings_layouts/device.py @@ -0,0 +1,60 @@ +from collections.abc import Callable + +from openpilot.selfdrive.ui.mici.layouts.settings.device import DeviceLayoutMici +from openpilot.selfdrive.ui.mici.widgets.button import BigCircleButton +from openpilot.selfdrive.ui.mici.widgets.dialog import BigDialog, BigConfirmationDialogV2 +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr + + +class DeviceLayoutMiciSP(DeviceLayoutMici): + def __init__(self, back_callback: Callable): + super().__init__(back_callback) + + self._enable_offroad = BigCircleButton("../../sunnypilot/selfdrive/assets/icons_mici/always_offroad.png", + red=True, icon_size=(120, 120)) + self._enable_offroad.set_click_callback(self._handle_always_offroad) + + self._disable_offroad = BigCircleButton("../../sunnypilot/selfdrive/assets/icons_mici/disable_offroad.png", + red=False, icon_size=(120, 120)) + self._disable_offroad.set_click_callback(self._handle_always_offroad) + + items = self._scroller._items.copy() + + items.insert(len(self._scroller._items) - 2, self._enable_offroad) + items.insert(len(self._scroller._items) - 2, self._disable_offroad) + + self._scroller._items.clear() + for item in items: + self._scroller.add_widget(item) + + def _handle_always_offroad(self): + if ui_state.engaged: + gui_app.set_modal_overlay(BigDialog(tr("disengage"),tr("\nto enable always offroad"))) + return + + def _enable_always_offroad(): + if not ui_state.engaged: + ui_state.params.put_bool("OffroadMode", True) + ui_state.always_offroad = True + + def _disable_always_offroad(): + ui_state.params.put_bool("OffroadMode", False) + ui_state.always_offroad = False + + if ui_state.params.get_bool("OffroadMode"): + dlg = BigConfirmationDialogV2(f"slide to exit always offroad", "icons_mici/settings/device/lkas.png", + red=True, confirm_callback=_disable_always_offroad) + else: + dlg = BigConfirmationDialogV2(tr("slide to enable always offroad"), "icons_mici/settings/device/lkas.png", + red=True, confirm_callback=_enable_always_offroad) + gui_app.set_modal_overlay(dlg) + + def _update_state(self): + super()._update_state() + + # Handle Always Offroad buttons + always_offroad = ui_state.always_offroad + self._enable_offroad.set_visible(not always_offroad) + self._disable_offroad.set_visible(always_offroad) diff --git a/selfdrive/ui/sunnypilot/ui_state.py b/selfdrive/ui/sunnypilot/ui_state.py index 4d26e138b1..8319ead7c9 100644 --- a/selfdrive/ui/sunnypilot/ui_state.py +++ b/selfdrive/ui/sunnypilot/ui_state.py @@ -143,6 +143,8 @@ class UIStateSP: self.true_v_ego_ui = self.params.get_bool("TrueVEgoUI") self.turn_signals = self.params.get_bool("ShowTurnSignals") self.boot_offroad_mode = self.params.get("DeviceBootMode", return_default=True) + self.always_offroad = self.params.get_bool("OffroadMode") + class DeviceSP: diff --git a/sunnypilot/selfdrive/assets/icons_mici/always_offroad.png b/sunnypilot/selfdrive/assets/icons_mici/always_offroad.png new file mode 100644 index 0000000000..56f35669c6 --- /dev/null +++ b/sunnypilot/selfdrive/assets/icons_mici/always_offroad.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e459241d896824f5e8207d568847acab3dedd41caae7af59d4c17e043663b0c9 +size 4035 diff --git a/sunnypilot/selfdrive/assets/icons_mici/disable_offroad.png b/sunnypilot/selfdrive/assets/icons_mici/disable_offroad.png new file mode 100644 index 0000000000..146734aafa --- /dev/null +++ b/sunnypilot/selfdrive/assets/icons_mici/disable_offroad.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27a0fca872d4586f578d246890b83674cdb7ecb03f58b2b0379b4b64a5816053 +size 3908