From 452aa675816b2f82e8baba80d9196993ac4218e6 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Dec 2025 15:20:24 -0500 Subject: [PATCH 1/6] DM: fix upstream merge overwrite with `latActive` check (#1594) * Update enabled condition to include latActive * Todo-sp --- selfdrive/monitoring/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/monitoring/helpers.py b/selfdrive/monitoring/helpers.py index 3377ce6c6..002e01ae3 100644 --- a/selfdrive/monitoring/helpers.py +++ b/selfdrive/monitoring/helpers.py @@ -449,7 +449,8 @@ class DriverMonitoring: rpyCalib = [0., 0., 0.] else: highway_speed = sm['carState'].vEgo - enabled = sm['selfdriveState'].enabled + # TODO-SP: unit test to assert both control checks are always present + enabled = sm['selfdriveState'].enabled or sm['carControl'].latActive wrong_gear = sm['carState'].gearShifter not in (car.CarState.GearShifter.drive, car.CarState.GearShifter.low) standstill = sm['carState'].standstill driver_engaged = sm['carState'].steeringPressed or sm['carState'].gasPressed From 1a1178140f614cb7fa6538a6d6cfeddba8b35599 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Dec 2025 21:35:51 -0500 Subject: [PATCH 2/6] ui: include MADS enabled state to `engaged` check (#1595) --- selfdrive/ui/ui_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index f44dc152b..0e1710c24 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -100,7 +100,7 @@ class UIState(UIStateSP): @property def engaged(self) -> bool: - return self.started and self.sm["selfdriveState"].enabled + return self.started and (self.sm["selfdriveState"].enabled or self.sm["selfdriveStateSP"].mads.enabled) def is_onroad(self) -> bool: return self.started From 09c4b933a82aadc2ab1577ad9db394d91fc80fd0 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Dec 2025 22:14:19 -0500 Subject: [PATCH 3/6] ui: capitalize button texts in Vehicle panel (#1597) --- selfdrive/ui/sunnypilot/layouts/settings/vehicle/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/sunnypilot/layouts/settings/vehicle/__init__.py b/selfdrive/ui/sunnypilot/layouts/settings/vehicle/__init__.py index 86d9e6169..0dd12d76f 100644 --- a/selfdrive/ui/sunnypilot/layouts/settings/vehicle/__init__.py +++ b/selfdrive/ui/sunnypilot/layouts/settings/vehicle/__init__.py @@ -23,7 +23,7 @@ class VehicleLayout(Widget): self._current_brand = None self._platform_selector = PlatformSelector(self._update_brand_settings) - self._vehicle_item = ListItemSP(title=self._platform_selector.text, action_item=ButtonAction(text=tr("Select")), + self._vehicle_item = ListItemSP(title=self._platform_selector.text, action_item=ButtonAction(text=tr("SELECT")), callback=self._platform_selector._on_clicked) self._vehicle_item.title_color = self._platform_selector.color self._legend_widget = LegendWidget(self._platform_selector) @@ -42,7 +42,7 @@ class VehicleLayout(Widget): def _update_brand_settings(self): self._vehicle_item._title = self._platform_selector.text self._vehicle_item.title_color = self._platform_selector.color - vehicle_text = tr("Remove") if ui_state.params.get("CarPlatformBundle") else tr("Select") + vehicle_text = tr("REMOVE") if ui_state.params.get("CarPlatformBundle") else tr("SELECT") self._vehicle_item.action_item.set_text(vehicle_text) brand = self.get_brand() From 8904300565b74e775df5ec00b95ca73a71488046 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Dec 2025 22:40:40 -0500 Subject: [PATCH 4/6] Toyota: Enforce Factory Longitudinal Control (#1596) * Toyota: enforce factory longitudinal control support * sunnylink! * bump * bruh --- common/params_keys.h | 1 + opendbc_repo | 2 +- .../layouts/settings/vehicle/brands/toyota.py | 44 +++++++++++++++++++ sunnypilot/selfdrive/car/interfaces.py | 7 ++- sunnypilot/sunnylink/params_metadata.json | 4 ++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 1b06711a9..7cfdf7540 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -213,6 +213,7 @@ inline static std::unordered_map keys = { {"SubaruStopAndGo", {PERSISTENT | BACKUP, BOOL, "0"}}, {"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}}, {"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}}, + {"ToyotaEnforceStockLongitudinal", {PERSISTENT | BACKUP, BOOL, "0"}}, {"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}}, {"BlindSpot", {PERSISTENT | BACKUP, BOOL, "0"}}, diff --git a/opendbc_repo b/opendbc_repo index a76d28a23..74ac67850 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit a76d28a231dd8a3de11ed47db2d185d3852c6925 +Subproject commit 74ac6785011b2861b822651f51d0cd2f01ce79d2 diff --git a/selfdrive/ui/sunnypilot/layouts/settings/vehicle/brands/toyota.py b/selfdrive/ui/sunnypilot/layouts/settings/vehicle/brands/toyota.py index e061a8a22..ac3d04f36 100644 --- a/selfdrive/ui/sunnypilot/layouts/settings/vehicle/brands/toyota.py +++ b/selfdrive/ui/sunnypilot/layouts/settings/vehicle/brands/toyota.py @@ -5,11 +5,55 @@ 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.sunnypilot.layouts.settings.vehicle.brands.base import BrandSettings +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, tr_noop +from openpilot.system.ui.widgets import DialogResult +from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog +from openpilot.system.ui.sunnypilot.widgets.list_view import toggle_item_sp + + +DESCRIPTIONS = { + 'enforce_stock_longitudinal': tr_noop( + 'sunnypilot will not take over control of gas and brakes. Factory Toyota longitudinal control will be used.' + ), +} class ToyotaSettings(BrandSettings): def __init__(self): super().__init__() + self.enforce_stock_longitudinal = toggle_item_sp( + lambda: tr("Enforce Factory Longitudinal Control"), + description=lambda: tr(DESCRIPTIONS["enforce_stock_longitudinal"]), + initial_state=ui_state.params.get_bool("ToyotaEnforceStockLongitudinal"), + callback=self._on_enable_enforce_stock_longitudinal, + enabled=lambda: not ui_state.engaged, + ) + + self.items = [self.enforce_stock_longitudinal, ] + + def _on_enable_enforce_stock_longitudinal(self, state: bool): + if state: + def confirm_callback(result: int): + if result == DialogResult.CONFIRM: + ui_state.params.put_bool("ToyotaEnforceStockLongitudinal", True) + if ui_state.params.get_bool("AlphaLongitudinalEnabled"): + ui_state.params.put_bool("AlphaLongitudinalEnabled", False) + ui_state.params.put_bool("OnroadCycleRequested", True) + else: + self.enforce_stock_longitudinal.action_item.set_state(False) + + content = (f"

{self.enforce_stock_longitudinal.title}


" + + f"

{self.enforce_stock_longitudinal.description}

") + + dlg = ConfirmDialog(content, tr("Enable"), rich=True) + gui_app.set_modal_overlay(dlg, callback=confirm_callback) + + else: + ui_state.params.put_bool("ToyotaEnforceStockLongitudinal", False) + ui_state.params.put_bool("OnroadCycleRequested", True) + def update_settings(self): pass diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index 83114ac55..a93f5724b 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -114,7 +114,7 @@ def initialize_params(params) -> list[dict[str, Any]]: # hyundai keys.extend([ - "HyundaiLongitudinalTuning" + "HyundaiLongitudinalTuning", ]) # subaru @@ -128,4 +128,9 @@ def initialize_params(params) -> list[dict[str, Any]]: "TeslaCoopSteering", ]) + # toyota + keys.extend([ + "ToyotaEnforceStockLongitudinal", + ]) + return [{k: params.get(k, return_default=True)} for k in keys] diff --git a/sunnypilot/sunnylink/params_metadata.json b/sunnypilot/sunnylink/params_metadata.json index 707e0620f..bbefc3d0c 100644 --- a/sunnypilot/sunnylink/params_metadata.json +++ b/sunnypilot/sunnylink/params_metadata.json @@ -1037,6 +1037,10 @@ "max": 5.0, "step": 0.1 }, + "ToyotaEnforceStockLongitudinal": { + "title": "Toyota: Enforce Factory Longitudinal Control", + "description": "When enabled, sunnypilot will not take over control of gas and brakes. Factory Toyota longitudinal control will be used." + }, "TrainingVersion": { "title": "Training Version", "description": "" From 6c6be573c7460a6ece20a81ff7043a1831565a9f Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Dec 2025 22:50:58 -0500 Subject: [PATCH 5/6] ui: `LineSeparatorSP` (#1598) --- system/ui/sunnypilot/widgets/list_view.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/system/ui/sunnypilot/widgets/list_view.py b/system/ui/sunnypilot/widgets/list_view.py index 2d7239ae6..cf69e9292 100644 --- a/system/ui/sunnypilot/widgets/list_view.py +++ b/system/ui/sunnypilot/widgets/list_view.py @@ -15,6 +15,7 @@ from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets.list_view import ListItem, ToggleAction, ItemAction, MultipleButtonAction, ButtonAction, \ _resolve_value, BUTTON_WIDTH, BUTTON_HEIGHT, TEXT_PADDING +from openpilot.system.ui.widgets.scroller_tici import LineSeparator, LINE_COLOR, LINE_PADDING from openpilot.system.ui.sunnypilot.lib.styles import style from openpilot.system.ui.sunnypilot.widgets.option_control import OptionControlSP, LABEL_WIDTH @@ -312,3 +313,15 @@ def button_item_sp(title: str | Callable[[], str], button_text: str | Callable[[ callback: Callable | None = None, enabled: bool | Callable[[], bool] = True) -> ListItemSP: action = ButtonActionSP(text=button_text, enabled=enabled) return ListItemSP(title=title, description=description, action_item=action, callback=callback) + + +class LineSeparatorSP(LineSeparator): + def __init__(self, height: int = 1): + super().__init__() + self._rect = rl.Rectangle(0, 0, 0, height) + + def _render(self, _): + line_y = int(self._rect.y + self._rect.height // 2) + rl.draw_line(int(self._rect.x) + LINE_PADDING, line_y, + int(self._rect.x + self._rect.width) - LINE_PADDING, line_y, + LINE_COLOR) From 9c7c84bd038de56cbd25f9630bfcaa72b695ba8e Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 20 Dec 2025 23:30:30 -0500 Subject: [PATCH 6/6] ui: simplify non-inline action button positioning in `ListViewSP` (#1599) ui: update non-inline action button positioning in `ListViewSP` --- system/ui/sunnypilot/widgets/list_view.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/system/ui/sunnypilot/widgets/list_view.py b/system/ui/sunnypilot/widgets/list_view.py index cf69e9292..6ba41f143 100644 --- a/system/ui/sunnypilot/widgets/list_view.py +++ b/system/ui/sunnypilot/widgets/list_view.py @@ -180,13 +180,8 @@ class ListItemSP(ListItem): return rl.Rectangle(0, 0, 0, 0) if not self.inline: - has_description = bool(self.description) and self.description_visible - - if has_description: - action_y = item_rect.y + self._text_size.y + style.ITEM_PADDING * 3 - else: - action_y = item_rect.y + item_rect.height - style.BUTTON_HEIGHT - style.ITEM_PADDING * 1.5 - + text_size = measure_text_cached(self._font, self.title, style.ITEM_TEXT_FONT_SIZE) + action_y = item_rect.y + text_size.y + style.ITEM_PADDING * 3 return rl.Rectangle(item_rect.x + style.ITEM_PADDING, action_y, item_rect.width - (style.ITEM_PADDING * 2), style.BUTTON_HEIGHT) right_width = self.action_item.get_width_hint()