Compare commits

...

57 Commits

Author SHA1 Message Date
royjr ba0f1859eb Update opendbc_repo 2026-10-28 23:21:19 -04:00
royjr 571a2fcb29 ui: make custom button action values contiguous 2026-10-28 23:14:30 -04:00
Jason Wen 093c73e785 HKG: move custom button into sunnypilot layouts 2026-10-28 21:57:15 -04:00
Jason Wen 1f20b4bc79 Merge master into hyundai-custom-button 2026-10-28 20:25:14 -04:00
royjr 20fcf35696 Merge branch 'master' into hyundai-custom-button 2026-10-27 16:10:18 -04:00
Jason Wen 08bf9b3511 Merge branch 'master' into hyundai-custom-button 2026-10-25 22:55:36 -04:00
Jason Wen c9316cc3d2 Merge branch 'master' into hyundai-custom-button
# Conflicts:
#	opendbc_repo
2026-10-25 22:50:04 -04:00
royjr 424311491c ui: preserve custom button presses 2026-08-16 21:14:39 -04:00
royjr a8795674b8 ui: remove custom button quiet mode 2026-08-16 16:33:39 -04:00
royjr 823b447fff ui: adapt custom button cycle by device 2026-08-16 16:29:12 -04:00
royjr c36db99c2d ui: cycle custom button navigation 2026-08-16 16:27:59 -04:00
royjr 5426b6af86 ui: toggle custom button navigation targets 2026-08-16 16:26:44 -04:00
royjr bf0cdd667b ui: add custom button navigation actions 2026-08-16 16:24:42 -04:00
royjr 9af59caf9c ui: compartmentalize custom button handling 2026-08-16 16:21:05 -04:00
royjr 1a75c53ea4 ui: handle custom steering button on comma 4 2026-08-16 16:18:09 -04:00
royjr 83999585ec Merge branch 'master' into hyundai-custom-button
# Conflicts:
#	opendbc_repo
#	selfdrive/ui/feedback/feedbackd.py
#	sunnypilot/sunnylink/params_metadata.json
2026-08-16 16:02:16 -04:00
royjr 138353adb4 Update opendbc_repo 2026-08-16 15:39:52 -04:00
royjr 40dc4e1591 Merge branch 'master' into hyundai-custom-button 2026-02-16 22:43:14 -05:00
royjr cce4810904 Update opendbc_repo 2026-02-16 22:43:09 -05:00
royjr 5b58f9c1f5 Update opendbc_repo 2026-02-13 23:34:16 -05:00
royjr 815ac7732b Merge branch 'master' into hyundai-custom-button 2026-02-13 23:02:12 -05:00
royjr 2321f9d8f5 Update opendbc_repo 2026-02-13 23:02:06 -05:00
royjr b9020e0003 Update params_metadata.json 2026-02-04 22:07:48 -05:00
royjr ceb466578a Update params_metadata.json 2026-02-04 22:07:19 -05:00
royjr e1cfea4924 Update RELEASES.md 2026-02-04 22:04:29 -05:00
royjr dd8a2bf83a Merge branch 'master' into hyundai-custom-button 2026-02-04 21:45:41 -05:00
royjr ba36a28f2a Update opendbc_repo 2026-02-04 21:43:53 -05:00
royjr cf2010389a Revert "Update opendbc_repo"
This reverts commit 43522c4922.
2026-02-04 19:14:43 -05:00
royjr 43522c4922 Update opendbc_repo 2026-02-04 19:14:28 -05:00
royjr 1cb5f24439 Merge branch 'master' into hyundai-custom-button 2025-10-08 21:37:43 -04:00
royjr b6a3f21a52 Merge branch 'master' into hyundai-custom-button 2025-10-08 18:27:27 -04:00
royjr 2ecc26b623 Update opendbc_repo 2025-10-08 18:27:19 -04:00
royjr 145f75fe06 Merge branch 'master' into hyundai-custom-button 2025-10-08 17:57:50 -04:00
royjr bd0578cbf9 Update opendbc_repo 2025-10-08 17:17:28 -04:00
royjr 40f24cc0b6 Update opendbc_repo 2025-09-03 10:08:01 -04:00
royjr 754efac063 Merge branch 'master' into hyundai-custom-button 2025-09-03 09:57:35 -04:00
royjr 132cc156f4 Update opendbc_repo 2025-09-03 09:57:27 -04:00
nayan 95ad932efb disable lkas per upstream & fix mixed events 2025-08-25 09:15:20 -04:00
nayan ae30f4119f Merge remote-tracking branch 'origin/master' into hyundai-custom-button
# Conflicts:
#	RELEASES.md
#	opendbc_repo
#	selfdrive/ui/feedback/feedbackd.py
2025-08-25 09:04:57 -04:00
nayan d251ae4976 pr be messing with pr 2025-08-24 16:40:48 -04:00
nayan 33324e590b use altButton2 event 2025-08-23 22:24:39 -04:00
nayan fea10ffd22 Merge remote-tracking branch 'origin/master' into hyundai-custom-button 2025-08-23 22:00:59 -04:00
nayan f1e11e3f06 bump opendbc 2025-08-23 22:00:50 -04:00
royjr c562eca8a1 Merge branch 'master' into hyundai-custom-button 2025-08-22 10:30:38 -04:00
royjr d9a690dac3 Update opendbc_repo 2025-08-22 10:30:28 -04:00
nayan a0eed058d1 bump opendbc 2025-08-13 17:28:11 -04:00
nayan abc9f47a6f Merge remote-tracking branch 'origin/master' into hyundai-custom-button
# Conflicts:
#	opendbc_repo
2025-08-13 17:27:19 -04:00
Nayan b064f6fcaf Merge branch 'master' into hyundai-custom-button 2025-08-12 14:41:16 -04:00
nayan 0c7d7df2ec keep both 2025-08-12 14:40:11 -04:00
nayan dc86f35957 refactor 2025-08-12 13:29:06 -04:00
nayan 9685b0aa9b lint 2025-08-12 12:49:17 -04:00
nayan f23cc408e3 not needed anymore 2025-08-12 12:48:49 -04:00
nayan cea00a6c14 todo 2025-08-12 12:00:53 -04:00
nayan f2949e1dc2 use custom button in feedbackd 2025-08-10 11:49:35 -04:00
royjr 5f1f34fa7f Update opendbc_repo 2025-08-10 09:31:11 -04:00
royjr 2c041f9025 Merge branch 'master' into hyundai-custom-button 2025-08-10 09:28:05 -04:00
nayan 74b2d519b9 move custom button to cs_sp, add bookmark functionality 2025-08-05 15:52:46 -04:00
12 changed files with 214 additions and 2 deletions
+1
View File
@@ -239,6 +239,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"BackupManager_RestoreVersion", {PERSISTENT, STRING}},
// sunnypilot car specific params
{"HyundaiCustomButtonAction", {PERSISTENT | BACKUP, INT, "0"}},
{"HyundaiLongitudinalTuning", {PERSISTENT | BACKUP, INT, "0"}},
{"SubaruStopAndGo", {PERSISTENT | BACKUP, BOOL, "0"}},
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
@@ -0,0 +1,38 @@
"""
Copyright (c) 2021-, 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 collections.abc import Callable
from enum import IntEnum
from opendbc.car.structs import car
from openpilot.selfdrive.ui.ui_state import ui_state
ALT_BUTTON_2_BIT = int(car.CarState.ButtonEvent.Type.altButton2)
class CustomButtonAction(IntEnum):
NONE = 0
BOOKMARK = 1
CYCLE_UI = 2
class CustomButton:
"""Runs the selected action on each custom button release, read from selfdriveStateSP.buttonsReleaseToggle."""
def __init__(self, callbacks: dict[int, Callable[[], None]]):
self._callbacks = callbacks
self._prev_toggle: int | None = None
def update(self) -> None:
if not (ui_state.started and ui_state.has_custom_button):
self._prev_toggle = None
return
toggle = ui_state.sm["selfdriveStateSP"].buttonsReleaseToggle
released = self._prev_toggle is not None and (self._prev_toggle ^ toggle) >> ALT_BUTTON_2_BIT & 1
self._prev_toggle = toggle
if released and (callback := self._callbacks.get(ui_state.params.get("HyundaiCustomButtonAction", return_default=True))):
callback()
@@ -0,0 +1,29 @@
"""
Copyright (c) 2021-, 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.main import MainLayout, MainState
from openpilot.selfdrive.ui.sunnypilot.custom_button import CustomButton, CustomButtonAction
class MainLayoutSP(MainLayout):
def __init__(self):
super().__init__()
self._custom_button = CustomButton({
CustomButtonAction.BOOKMARK: self._on_bookmark_clicked,
CustomButtonAction.CYCLE_UI: self._cycle_ui,
})
def _render(self, rect):
self._custom_button.update()
super()._render(rect)
def _cycle_ui(self):
if self._current_mode == MainState.ONROAD and not self._sidebar.is_visible:
self._sidebar.set_visible(True)
elif self._current_mode == MainState.SETTINGS:
self._set_mode_for_state()
else:
self._on_settings_clicked()
@@ -20,7 +20,13 @@ class HyundaiSettings(BrandSettings):
self.longitudinal_tuning_item = multiple_button_item_sp(tr("Custom Longitudinal Tuning"), "", tuning_texts,
button_width=300, callback=self._on_tuning_selected,
param="HyundaiLongitudinalTuning", inline=False)
self.items = [self.longitudinal_tuning_item]
custom_button_texts = [tr("None"), tr("Bookmark"), tr("Cycle UI")]
self.custom_button_item = multiple_button_item_sp(tr("Steering Custom Button"),
tr("Choose the sunnypilot action for the steering wheel custom/star button."),
custom_button_texts, button_width=300,
param="HyundaiCustomButtonAction", inline=False)
self.items = [self.custom_button_item, self.longitudinal_tuning_item]
@staticmethod
def _on_tuning_selected(index):
@@ -57,3 +63,7 @@ class HyundaiSettings(BrandSettings):
self.longitudinal_tuning_item.show_description(True)
self.longitudinal_tuning_item.action_item.set_selected_button(tuning_param)
self.longitudinal_tuning_item.set_visible(self.alpha_long_available)
self.custom_button_item.action_item.set_selected_button(ui_state.params.get("HyundaiCustomButtonAction", return_default=True))
self.custom_button_item.show_description(True)
self.custom_button_item.set_visible(ui_state.has_custom_button)
@@ -0,0 +1,39 @@
"""
Copyright (c) 2021-, 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.mici.layouts.main import MiciMainLayout
from openpilot.selfdrive.ui.sunnypilot.custom_button import CustomButton, CustomButtonAction
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.widgets import Widget
class MiciMainLayoutSP(MiciMainLayout):
def __init__(self):
super().__init__()
self._custom_button = CustomButton({
CustomButtonAction.BOOKMARK: self._on_bookmark_clicked,
CustomButtonAction.CYCLE_UI: self._cycle_ui,
})
def _render(self, rect):
self._custom_button.update()
super()._render(rect)
def _show_layout(self, layout: Widget):
if gui_app.widget_in_stack(self._onboarding_window):
return
gui_app.pop_widgets_to(self, lambda: self._scroll_to(layout))
def _layout_visible(self, layout: Widget) -> bool:
return abs(layout.rect.x - self._rect.x) < self._rect.width / 2
def _cycle_ui(self):
if gui_app.widget_in_stack(self._settings_layout):
self._show_layout(self._onroad_layout)
elif gui_app.get_active_widget() is self and self._layout_visible(self._home_layout):
gui_app.push_widget(self._settings_layout)
else:
self._show_layout(self._home_layout)
@@ -9,6 +9,7 @@ from enum import Enum
from openpilot.cereal import messaging, log, custom
from opendbc.car.structs import car
from opendbc.sunnypilot.car.interfaces import has_bsm
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
from openpilot.common.params import Params
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import OnroadBrightness
from openpilot.sunnypilot.models.helpers import ACTIVE_BUNDLE_KEYS, get_active_source
@@ -33,6 +34,7 @@ class UIStateSP:
self.params = Params()
self.CP_SP: custom.CarParamsSP | None = None
self.has_icbm: bool = False
self.has_custom_button: bool = False
self.is_sp_release: bool = self.params.get_bool("IsReleaseSpBranch")
self.sm_services_ext = [
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
@@ -160,6 +162,8 @@ class UIStateSP:
if CP_SP_bytes is not None:
self.CP_SP = messaging.log_from_bytes(CP_SP_bytes, custom.CarParamsSP)
self.has_icbm = self.CP_SP.intelligentCruiseButtonManagementAvailable and self.params.get_bool("IntelligentCruiseButtonManagement")
# flagsSP bits are per brand
self.has_custom_button = self.CP is not None and self.CP.brand == "hyundai" and bool(self.CP_SP.flags & HyundaiFlagsSP.HAS_CUSTOM_BUTTON)
self._enforce_constraints()
source = get_active_source(chestnut=self.chestnut_present, chestnut_active=self.chestnut_active,
@@ -0,0 +1,41 @@
from types import SimpleNamespace
from unittest.mock import Mock
from openpilot.common.test import OpenpilotTestCase
import openpilot.selfdrive.ui.sunnypilot.custom_button as custom_button
from openpilot.selfdrive.ui.sunnypilot.custom_button import ALT_BUTTON_2_BIT, CustomButton, CustomButtonAction
def make_ui_state(monkeypatch, action, has_custom_button=True):
state = SimpleNamespace(started=True, has_custom_button=has_custom_button, params=Mock(),
sm={"selfdriveStateSP": SimpleNamespace(buttonsReleaseToggle=0)})
state.params.get.return_value = action
monkeypatch.setattr(custom_button, "ui_state", state)
return state
def run(button, state, toggles):
for toggle in toggles:
state.sm["selfdriveStateSP"].buttonsReleaseToggle = toggle
button.update()
class TestCustomButton(OpenpilotTestCase):
def test_actions_on_release(self, monkeypatch):
for action in (CustomButtonAction.BOOKMARK, CustomButtonAction.CYCLE_UI):
state = make_ui_state(monkeypatch, action)
callbacks = {a: Mock() for a in (CustomButtonAction.BOOKMARK, CustomButtonAction.CYCLE_UI)}
# first read seeds, then two releases of altButton2 and one of another button
run(CustomButton(callbacks), state, [0, 1 << ALT_BUTTON_2_BIT, 1 << ALT_BUTTON_2_BIT, 0, 0b1])
assert callbacks[action].call_count == 2
assert sum(cb.call_count for cb in callbacks.values()) == 2
def test_no_action_when_unavailable(self, monkeypatch):
callback = Mock()
state = make_ui_state(monkeypatch, CustomButtonAction.BOOKMARK, has_custom_button=False)
run(CustomButton({CustomButtonAction.BOOKMARK: callback}), state, [0, 1 << ALT_BUTTON_2_BIT])
state = make_ui_state(monkeypatch, CustomButtonAction.BOOKMARK)
run(CustomButton({CustomButtonAction.BOOKMARK: callback}), state, [1 << ALT_BUTTON_2_BIT]) # stale toggle on first read
assert callback.call_count == 0
+4
View File
@@ -10,6 +10,10 @@ from openpilot.selfdrive.ui.layouts.main import MainLayout
from openpilot.selfdrive.ui.mici.layouts.main import MiciMainLayout
from openpilot.selfdrive.ui.ui_state import ui_state
if gui_app.sunnypilot_ui():
from openpilot.selfdrive.ui.sunnypilot.layouts.main import MainLayoutSP as MainLayout
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.main import MiciMainLayoutSP as MiciMainLayout
BIG_UI = gui_app.big_ui()
@@ -11,6 +11,7 @@ from opendbc.car.structs import car
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR, UNSUPPORTED_LONGITUDINAL_CAR
from opendbc.car.subaru.values import CAR as SUBARU_CAR, SubaruFlags
from opendbc.car.toyota.values import CAR as TOYOTA_CAR, ToyotaFlags
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
from opendbc.sunnypilot.car.interfaces import has_bsm
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP
from openpilot.common.params import Params
@@ -44,6 +45,7 @@ CAPABILITY_FIELDS = (
"device_type",
"subaru_has_sng",
"hyundai_alpha_long_available",
"hyundai_has_custom_button",
"toyota_tss2",
"custom_model_active",
)
@@ -68,6 +70,7 @@ CAPABILITY_LABELS: dict[str, str] = {
"device_type": "Device type",
"subaru_has_sng": "Subaru Stop-and-Go available",
"hyundai_alpha_long_available": "Hyundai Alpha Longitudinal available",
"hyundai_has_custom_button": "Hyundai steering wheel custom button",
"toyota_tss2": "Toyota TSS2 platform",
"custom_model_active": "Non-Default model selected",
}
@@ -192,6 +195,7 @@ def generate_capabilities(params: Params | None = None) -> dict:
caps["icbm_available"] = bool(CP_SP.intelligentCruiseButtonManagementAvailable)
caps["has_icbm"] = bool(CP_SP.intelligentCruiseButtonManagementAvailable) and params.get_bool("IntelligentCruiseButtonManagement")
caps["tesla_has_vehicle_bus"] = bool(CP_SP.flags & TeslaFlagsSP.HAS_VEHICLE_BUS)
caps["hyundai_has_custom_button"] = caps["brand"] == "hyundai" and bool(CP_SP.flags & HyundaiFlagsSP.HAS_CUSTOM_BUTTON)
except Exception:
cloudlog.exception("capabilities: failed to deserialize CarParamsSPPersistent")
@@ -2174,6 +2174,33 @@
"title": "Hyundai / Kia / Genesis Settings",
"description": "",
"items": [
{
"key": "HyundaiCustomButtonAction",
"widget": "multiple_button",
"title": "Steering Custom Button",
"description": "Choose the sunnypilot action for the steering wheel custom/star button.",
"options": [
{
"value": 0,
"label": "None"
},
{
"value": 1,
"label": "Bookmark"
},
{
"value": 2,
"label": "Cycle UI"
}
],
"visibility": [
{
"type": "capability",
"field": "hyundai_has_custom_button",
"equals": true
}
]
},
{
"key": "HyundaiLongitudinalTuning",
"widget": "multiple_button",
@@ -10,6 +10,21 @@ sections:
title: Hyundai / Kia / Genesis Settings
description: ''
items:
- key: HyundaiCustomButtonAction
widget: multiple_button
title: Steering Custom Button
description: Choose the sunnypilot action for the steering wheel custom/star button.
options:
- value: 0
label: None
- value: 1
label: Bookmark
- value: 2
label: Cycle UI
visibility:
- type: capability
field: hyundai_has_custom_button
equals: true
- key: HyundaiLongitudinalTuning
widget: multiple_button
title: Custom Longitudinal Tuning