From a307a085917d98f72a519573d02693179bfd2819 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 23 Mar 2025 15:50:21 -0400 Subject: [PATCH 1/4] ui: fix offset with `OptionControlSP` for macOS (#705) ui: fix offset with `OptionControlSP` --- selfdrive/ui/sunnypilot/qt/widgets/controls.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index a7c60980fa..1578130bbb 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -520,7 +520,11 @@ protected: } // Draw the rectangle +#ifdef __APPLE__ + QRect rect(0, !_title.isEmpty() ? (h - 16) : 20, w, h); +#else QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h); +#endif p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color p.setPen(QPen(Qt::NoPen)); p.drawRoundedRect(rect, 20, 20); From 96d73fe2b33633bbca788ef25c1e465ca9637fb9 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 23 Mar 2025 16:04:12 -0400 Subject: [PATCH 2/4] NNLC: bump max similarity for higher accuracy (#704) Update helpers.py Co-authored-by: Jason Wen --- sunnypilot/selfdrive/controls/lib/nnlc/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/helpers.py b/sunnypilot/selfdrive/controls/lib/nnlc/helpers.py index 0ea920f198..8c33fec023 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/helpers.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/helpers.py @@ -46,10 +46,10 @@ def get_nn_model_path(CP: structs.CarParams) -> tuple[str, str, bool]: exact_match = max_similarity >= 0.99 - if car_fingerprint not in model_path or 0.0 <= max_similarity < 0.8: + if car_fingerprint not in model_path or 0.0 <= max_similarity < 0.9: nn_candidate = car_fingerprint model_path, max_similarity = check_nn_path(nn_candidate) - if 0.0 <= max_similarity < 0.8: + if 0.0 <= max_similarity < 0.9: with open(TORQUE_NN_MODEL_SUBSTITUTE_PATH, 'rb') as f: sub = tomllib.load(f) sub_candidate = sub.get(car_fingerprint, car_fingerprint) From 982674b4a71e900fe7263371390d4316d6f99f46 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 23 Mar 2025 22:54:40 -0400 Subject: [PATCH 3/4] ui: support dynamic state updates for `PushButtonSP` (#708) * ui: support dynamic state updates for `PushButtonSP` * test btn * override mouse release events * Revert "test btn" This reverts commit cd9c9dde9a37a8b2c7de7ad123cbb53f80e9e5e6. * Reapply "test btn" This reverts commit 9b36b2e08500d81cecb95f6c47a66a43e2dcd1b4. * abstract param flipping * Revert "Reapply "test btn"" This reverts commit 8104a262b02303b1c6fe2df00b2d65408fa010ea. * update device panel handling for the new param --- .../qt/offroad/settings/device_panel.cc | 22 ++++--- selfdrive/ui/sunnypilot/qt/widgets/controls.h | 66 +++++++++++++++---- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc index ca6d6c62fa..eef025b0a5 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc @@ -16,23 +16,25 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) { device_grid_layout->setHorizontalSpacing(5); device_grid_layout->setVerticalSpacing(25); - std::vector> device_btns = { - {"dcamBtn", tr("Driver Camera Preview")}, - {"retrainingBtn", tr("Training Guide")}, - {"regulatoryBtn", tr("Regulatory")}, - {"translateBtn", tr("Language")}, - {"resetParams", tr("Reset Settings")}, + std::vector> device_btns = { + {"dcamBtn", tr("Driver Camera Preview"), ""}, + {"retrainingBtn", tr("Training Guide"), ""}, + {"regulatoryBtn", tr("Regulatory"), ""}, + {"translateBtn", tr("Language"), ""}, + {"resetParams", tr("Reset Settings"), ""}, }; int row = 0, col = 0; - for (int i = 0; i < device_btns.size(); i++) { - if (device_btns[i].first == "regulatoryBtn" && !Hardware::TICI()) { + for (const auto &[id, text, param] : device_btns) { + if (id == "regulatoryBtn" && !Hardware::TICI()) { continue; } - auto *btn = new PushButtonSP(device_btns[i].second, 720, this); + auto *btn = new PushButtonSP(text, 720, this, param); + btn->setObjectName(id); + device_grid_layout->addWidget(btn, row, col); - buttons[device_btns[i].first] = btn; + buttons[id] = btn; col++; if (col > 1) { diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index 1578130bbb..acbc3ea3c4 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -554,8 +554,8 @@ class PushButtonSP : public QPushButton { Q_OBJECT public: - PushButtonSP(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) { - const QString buttonStyle = R"( + PushButtonSP(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr, const QString ¶m = "") : QPushButton(text, parent) { + buttonStyle = R"( QPushButton { border-radius: 20px; font-size: 50px; @@ -564,21 +564,61 @@ public: padding: 0 25px 0 25px; color: #FFFFFF; } - QPushButton:enabled { - background-color: #393939; - } - QPushButton:pressed { - background-color: #4A4A4A; - } - QPushButton:disabled { - background-color: #121212; - color: #5C5C5C; - } )"; - setStyleSheet(buttonStyle); + if (!param.isEmpty()) { + key = param.toStdString(); + refresh(); + } else { + updateStyle(false); + } + setFixedWidth(minimum_button_width); } + + void refresh() { + if (!key.empty()) { + bool state = params.getBool(key); + if (state != is_enabled) { + is_enabled = state; + } + updateStyle(is_enabled); + } + } + + void updateButton() { + if (!key.empty()) { + params.putBool(key, !is_enabled); + refresh(); + } + } + +protected: + // Override mouse release event to handle style updates smoothly + void mouseReleaseEvent(QMouseEvent *event) override { + if (!key.empty()) { + bool next_state = !params.getBool(key); + updateStyle(next_state); + } + QPushButton::mouseReleaseEvent(event); + } + +private: + std::string key = ""; + Params params; + bool is_enabled; + QString buttonStyle; + + QString btn_enabled_off_style = "QPushButton:enabled { background-color: #393939; }"; + QString btn_enabled_on_style = "QPushButton:enabled { background-color: #1e79e8; }"; + QString btn_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }"; + QString btn_disabled_stype = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }"; + + void updateStyle(bool enabled) { + QString enabled_style = enabled ? btn_enabled_on_style : btn_enabled_off_style; + + setStyleSheet(buttonStyle + enabled_style + btn_pressed_style + btn_disabled_stype); + } }; class PanelBackButton : public QPushButton { From 029a601674d5647508f93ea81abcd9fc94a20427 Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Sun, 23 Mar 2025 20:15:11 -0700 Subject: [PATCH 4/4] Device: Quiet Mode (#654) * init quiet mode * only for sunny * static * toggle * let's back this up * review sugg * oh okay * review * fix: ensure boolean conversion for QuietDrive parameter * Refactor return statement to use boolean conversion for clarity in quiet mode logic * Update selfdrive/ui/sunnypilot/quietmode.py * rename * sunny * Revert "sunny" This reverts commit 6ac4cf4b8d3b0f8576cdec0146f3815a662a01a5. * sunny * Revert "sunny" This reverts commit c2bffddc052bdc2e269f3f7070934037c490810a. * sunny * ui: support dynamic state updates for `PushButtonSP` * test btn * override mouse release events * Revert "test btn" This reverts commit cd9c9dde9a37a8b2c7de7ad123cbb53f80e9e5e6. * Reapply "test btn" This reverts commit 9b36b2e08500d81cecb95f6c47a66a43e2dcd1b4. * abstract param flipping * Revert "Reapply "test btn"" This reverts commit 8104a262b02303b1c6fe2df00b2d65408fa010ea. * use new button state for PushButtonSP * Quiet Drive -> Quiet Mode * driver camera btn moved --------- Co-authored-by: Jason Wen --- common/params_keys.h | 1 + selfdrive/ui/soundd.py | 10 ++++- .../qt/offroad/settings/device_panel.cc | 3 ++ selfdrive/ui/sunnypilot/quiet_mode.py | 39 +++++++++++++++++++ selfdrive/ui/tests/test_ui/run.py | 2 +- system/manager/manager.py | 1 + 6 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/quiet_mode.py diff --git a/common/params_keys.h b/common/params_keys.h index 8237bdabb1..710a354a68 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -128,6 +128,7 @@ inline static std::unordered_map keys = { {"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION}, {"OffroadMode", CLEAR_ON_MANAGER_START}, {"OffroadMode_Status", CLEAR_ON_MANAGER_START}, + {"QuietMode", PERSISTENT | BACKUP}, // MADS params {"Mads", PERSISTENT | BACKUP}, diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 8b5fc8bd04..facb398339 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -13,6 +13,8 @@ from openpilot.common.swaglog import cloudlog from openpilot.system import micd +from openpilot.selfdrive.ui.sunnypilot.quiet_mode import QuietMode + SAMPLE_RATE = 48000 SAMPLE_BUFFER = 4096 # (approx 100ms) MAX_VOLUME = 1.0 @@ -50,8 +52,10 @@ def check_selfdrive_timeout_alert(sm): return False -class Soundd: +class Soundd(QuietMode): def __init__(self): + super().__init__() + self.load_sounds() self.current_alert = AudibleAlert.none @@ -81,7 +85,7 @@ class Soundd: ret = np.zeros(frames, dtype=np.float32) - if self.current_alert != AudibleAlert.none: + if self.should_play_sound(self.current_alert): num_loops = sound_list[self.current_alert][1] sound_data = self.loaded_sounds[self.current_alert] written_frames = 0 @@ -144,6 +148,8 @@ class Soundd: while True: sm.update(0) + self.load_param() + if sm.updated['microphone'] and self.current_alert == AudibleAlert.none: # only update volume filter when not playing alert self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb) self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x)) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc index eef025b0a5..f26ed0a198 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc @@ -17,6 +17,7 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) { device_grid_layout->setVerticalSpacing(25); std::vector> device_btns = { + {"quietModeBtn", tr("Quiet Mode"), "QuietMode"}, {"dcamBtn", tr("Driver Camera Preview"), ""}, {"retrainingBtn", tr("Training Guide"), ""}, {"regulatoryBtn", tr("Regulatory"), ""}, @@ -45,6 +46,8 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) { connect(buttons["dcamBtn"], &PushButtonSP::clicked, [=]() { emit showDriverView(); }); + connect(buttons["quietModeBtn"], &PushButtonSP::clicked, buttons["quietModeBtn"], &PushButtonSP::updateButton); + connect(buttons["retrainingBtn"], &PushButtonSP::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) { emit reviewTrainingGuide(); diff --git a/selfdrive/ui/sunnypilot/quiet_mode.py b/selfdrive/ui/sunnypilot/quiet_mode.py new file mode 100644 index 0000000000..893df95033 --- /dev/null +++ b/selfdrive/ui/sunnypilot/quiet_mode.py @@ -0,0 +1,39 @@ +""" +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 cereal import car + +from openpilot.common.params import Params + +AudibleAlert = car.CarControl.HUDControl.AudibleAlert + +ALERTS_ALWAYS_PLAY = { + AudibleAlert.warningSoft, + AudibleAlert.warningImmediate, + AudibleAlert.promptDistracted, + AudibleAlert.promptRepeat, +} + +class QuietMode: + def __init__(self): + self.params = Params() + self.enabled: bool = self.params.get_bool("QuietMode") + self._frame = 0 + + def load_param(self) -> None: + self._frame += 1 + if self._frame % 50 == 0: # 2.5 seconds + self.enabled = self.params.get_bool("QuietMode") + + def should_play_sound(self, current_alert: int) -> bool: + """ + Check if a sound should be played based on the Quiet Mode setting + and the current alert. + """ + if not self.enabled: + return bool(current_alert != AudibleAlert.none) + + return current_alert in ALERTS_ALWAYS_PLAY diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 1d7437bf65..c28b578d95 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -151,7 +151,7 @@ def setup_keyboard_uppercase(click, pm: PubMaster, scroll=None): def setup_driver_camera(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) - click(950, 620) + click(1720, 620) DATA['deviceState'].deviceState.started = False setup_onroad(click, pm) DATA['deviceState'].deviceState.started = True diff --git a/system/manager/manager.py b/system/manager/manager.py index 9266a62aae..bcc2ff5c28 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -51,6 +51,7 @@ def manager_init() -> None: ("ModelManager_LastSyncTime", "0"), ("ModelManager_ModelsCache", ""), ("NeuralNetworkLateralControl", "0"), + ("QuietMode", "0"), ] if params.get_bool("RecordFrontLock"):