From cdca40f018c3d0f8eac02026c374be4c20a16258 Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Sun, 31 May 2026 04:27:57 -0400 Subject: [PATCH] BigUI WIP: Sound Panel Alignment --- .../layouts/settings/starpilot/aethergrid.py | 28 ++- .../ui/layouts/settings/starpilot/sounds.py | 233 ++++++++++++------ 2 files changed, 182 insertions(+), 79 deletions(-) diff --git a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py index 0043d6b27..23076ac14 100644 --- a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py +++ b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py @@ -227,7 +227,7 @@ class AetherListMetrics: toggle_right_inset: int = 34 adjustor_row_height: int = 94 adjustor_row_active_height: int = 154 - adjustor_preset_height: int = 30 + adjustor_preset_height: int = 44 adjustor_preset_gap: int = 10 adjustor_scrubber_height: int = 52 adjustor_value_pill_height: int = 36 @@ -1150,7 +1150,7 @@ class AetherInlineRangeControl(Widget): return self._pending_drag = True - self._started_on_thumb = rl.check_collision_point_rec(mouse_pos, _inflate_rect(self._thumb_rect, 8, 8)) + self._started_on_thumb = rl.check_collision_point_rec(mouse_pos, _inflate_rect(self._thumb_rect, 12, 12)) self._press_start = rl.Vector2(mouse_pos.x, mouse_pos.y) def _handle_mouse_release(self, mouse_pos: MousePos): @@ -1269,7 +1269,7 @@ class AetherInlineRangeControl(Widget): fill_rect = _snap_rect(rl.Rectangle(self._track_rect.x, self._track_rect.y, fill_w, self._track_rect.height)) rl.draw_rectangle_rounded(fill_rect, 1.0, 12, _with_alpha(self._color, 220)) - thumb_w = 18 + self._thumb_focus * 4 + thumb_w = 22 + self._thumb_focus * 4 thumb_h = 28 + self._thumb_focus * 4 thumb_center_x = self._track_rect.x + fill_frac * self._track_rect.width thumb_center_y = rect.y + rect.height / 2 @@ -1534,7 +1534,7 @@ class AetherAdjustorRow(Widget): self._preset_rects.clear() if self._presets: - chip_gap = 8.0 + chip_gap = 12.0 chip_h = float(AETHER_LIST_METRICS.adjustor_preset_height) chip_w = max(68.0, (rect.width - 48 - chip_gap * (len(self._presets) - 1)) / max(1, len(self._presets))) chip_x = content_left @@ -2346,7 +2346,7 @@ class AetherTile(Widget): self, face: rl.Rectangle, *, - icon: rl.Texture2D | None, + icon: rl.Texture2D | None = None, title: str, primary: str, desc: str, @@ -3477,9 +3477,9 @@ class TileGrid(Widget): tile_idx += 1 -def draw_toggle_pill(rect: rl.Rectangle, is_on: bool, is_enabled: bool, title: str, status_str: str, hovered: bool, pressed: bool, style: PanelStyle = DEFAULT_PANEL_STYLE): +def draw_toggle_pill(rect: rl.Rectangle, is_on: bool, is_enabled: bool, title: str, status_str: str, hovered: bool, pressed: bool, style: PanelStyle = DEFAULT_PANEL_STYLE, *, subtitle: str = ""): rect = _snap_rect(rect) - bg_color = rl.Color(28, 32, 40, 170 if not is_enabled else 255) + bg_color = rl.Color(28, 32, 40, 200 if not is_enabled else 255) accent = style.accent if is_on and is_enabled else rl.Color(255, 255, 255, 52 if is_enabled else 20) _draw_rounded_fill(rect, bg_color, radius_px=18) _draw_rounded_stroke(rect, _with_alpha(accent, 92 if is_on and is_enabled else accent.a), radius_px=18) @@ -3494,9 +3494,17 @@ def draw_toggle_pill(rect: rl.Rectangle, is_on: bool, is_enabled: bool, title: s font = gui_app.font(FontWeight.BOLD) title_size = max(16, min(22, int(rect.height * 0.26))) status_size = max(18, min(24, int(rect.height * 0.32))) - title_y = rect.y + (rect.height - title_size) / 2 - rl.draw_text_ex(font, title, rl.Vector2(round(rect.x + 24), round(title_y)), title_size, 0, AetherListColors.SUBTEXT if is_enabled else AetherListColors.MUTED) - + + if subtitle: + title_y = rect.y + (rect.height - title_size - 18) / 2 + rl.draw_text_ex(font, title, rl.Vector2(round(rect.x + 24), round(title_y)), title_size, 0, AetherListColors.SUBTEXT if is_enabled else AetherListColors.MUTED) + sub_font = gui_app.font(FontWeight.NORMAL) + sub_size = max(12, min(16, int(rect.height * 0.18))) + rl.draw_text_ex(sub_font, subtitle, rl.Vector2(round(rect.x + 24), round(title_y + title_size + 2)), sub_size, 0, AetherListColors.MUTED) + else: + title_y = rect.y + (rect.height - title_size) / 2 + rl.draw_text_ex(font, title, rl.Vector2(round(rect.x + 24), round(title_y)), title_size, 0, AetherListColors.SUBTEXT if is_enabled else AetherListColors.MUTED) + ts = measure_text_cached(font, status_str, status_size) status_x = rect.x + rect.width - ts.x - 24 rl.draw_text_ex(font, status_str, rl.Vector2(round(status_x), round(rect.y + (rect.height - ts.y) / 2)), status_size, 0, AetherListColors.HEADER if is_enabled else AetherListColors.MUTED) diff --git a/selfdrive/ui/layouts/settings/starpilot/sounds.py b/selfdrive/ui/layouts/settings/starpilot/sounds.py index 3f913db09..cbf9dc63a 100644 --- a/selfdrive/ui/layouts/settings/starpilot/sounds.py +++ b/selfdrive/ui/layouts/settings/starpilot/sounds.py @@ -1,4 +1,5 @@ from __future__ import annotations +from dataclasses import replace import subprocess from pathlib import Path @@ -6,30 +7,49 @@ import pyray as rl from openpilot.common.basedir import BASEDIR from openpilot.starpilot.common.starpilot_variables import ACTIVE_THEME_PATH -from openpilot.system.ui.lib.application import gui_app, MouseEvent, MousePos +from openpilot.system.ui.lib.application import gui_app, FontWeight, MouseEvent, MousePos from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.lib.scroll_panel2 import GuiScrollPanel2 from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.label import gui_label from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import _SettingsPage from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import ( AETHER_LIST_METRICS, + COMPACT_PANEL_METRICS, AetherAdjustorRow, AetherListColors, AetherScrollbar, + TileGrid, + ToggleTile, panel_style_from_color, _point_hits, + draw_action_pill, + draw_list_group_shell, draw_list_scroll_fades, draw_section_header, draw_settings_panel_header, - draw_toggle_pill, init_list_panel, ) PANEL_STYLE = panel_style_from_color("#E63956") SECTION_GAP = AETHER_LIST_METRICS.section_gap +SECTION_HEADER_HEIGHT = AETHER_LIST_METRICS.section_header_height +SECTION_HEADER_GAP = AETHER_LIST_METRICS.section_header_gap +GROUP_HEADER_HEIGHT = 20 +GROUP_HEADER_GAP = 4 +GROUP_HAIRLINE_COLOR = rl.Color(255, 255, 255, 10) +GROUP_HEADER_COLOR = rl.Color(255, 255, 255, 90) + +SOUNDS_PANEL_METRICS = replace( + COMPACT_PANEL_METRICS, + outer_margin_y=14, + panel_padding_top=16, + panel_padding_bottom=14, + header_height=88, +) class SoundsManagerView(Widget): @@ -39,17 +59,19 @@ class SoundsManagerView(Widget): self._pressed_target: str | None = None self._adjustor_rows: dict[str, AetherAdjustorRow] = {} self._was_interacting: dict[str, bool] = {} - self._toggle_rects: dict[str, rl.Rectangle] = {} self._active_adjustor_key: str | None = None self._can_click = True + self._reset_rect = rl.Rectangle(0, 0, 0, 0) self._scroll_panel = GuiScrollPanel2(horizontal=False) self._scrollbar = AetherScrollbar() self._content_height = 0.0 self._scroll_offset = 0.0 self._scroll_rect = rl.Rectangle(0, 0, 0, 0) + self._tile_grid_h = 0.0 self._init_adjustors() + self._init_toggles() self._forward_touch_valid() def _forward_touch_valid(self): @@ -57,6 +79,25 @@ class SoundsManagerView(Widget): adjustor.set_touch_valid_callback( lambda: self._scroll_panel.is_touch_valid() or adjustor.is_interacting ) + self._toggle_grid.set_touch_valid_callback( + lambda: self._scroll_panel.is_touch_valid() + ) + + def _init_toggles(self): + self._toggle_grid = TileGrid(columns=2, padding=12) + for key in self._controller.CUSTOM_ALERTS_KEYS: + info = self._controller.ALERT_INFO[key] + tile = ToggleTile( + title=tr(info["title"]), + get_state=lambda k=key: self._controller._params.get_bool(k), + set_state=lambda state, k=key: self._controller._params.put_bool(k, state), + bg_color=PANEL_STYLE.accent, + desc=tr(info.get("subtitle", "")), + is_enabled=info.get("is_enabled"), + disabled_label=tr(info.get("disabled_label", "")) if info.get("disabled_label") else "", + ) + self._toggle_grid.add_tile(tile) + self._child(self._toggle_grid) def _set_active_adjustor(self, key: str, active: bool): if active: @@ -80,7 +121,7 @@ class SoundsManagerView(Widget): adjustor = AetherAdjustorRow( tr(info["title"]), - "", + tr(info["subtitle"]), 0.0, 101.0, 1.0, get_value=lambda k=key: float(self._controller._params.get_int(k, return_default=True, default=100)), on_change=on_change, @@ -96,13 +137,14 @@ class SoundsManagerView(Widget): self._adjustor_rows[key] = adjustor cd_key = self._controller.COOLDOWN_KEY + cd_info = self._controller.COOLDOWN_INFO def on_cd_commit(v): self._controller._params.put_int(cd_key, int(v)) cd_adjustor = AetherAdjustorRow( - tr(self._controller.COOLDOWN_INFO["title"]), - "", - 0.0, float(self._controller.COOLDOWN_INFO["max"]), 1.0, + tr(cd_info["title"]), + tr(cd_info["subtitle"]), + 0.0, float(cd_info["max"]), 1.0, get_value=lambda: float(self._controller._params.get_int(cd_key, return_default=True, default=0)), on_change=lambda _v: None, on_commit=on_cd_commit, @@ -121,10 +163,12 @@ class SoundsManagerView(Widget): self._can_click = True for adjustor in self._adjustor_rows.values(): adjustor._handle_mouse_press(mouse_pos) + self._toggle_grid._handle_mouse_press(mouse_pos) def _handle_mouse_release(self, mouse_pos: MousePos): for adjustor in self._adjustor_rows.values(): adjustor._handle_mouse_release(mouse_pos) + self._toggle_grid._handle_mouse_release(mouse_pos) target = self._target_at(mouse_pos) if self._scroll_panel.is_touch_valid() else None if self._pressed_target is not None and self._pressed_target == target and self._can_click: @@ -138,20 +182,16 @@ class SoundsManagerView(Widget): return for adjustor in self._adjustor_rows.values(): adjustor._handle_mouse_event(mouse_event) + self._toggle_grid._handle_mouse_event(mouse_event) def _target_at(self, mouse_pos: MousePos) -> str | None: - for key, rect in self._toggle_rects.items(): - if _point_hits(mouse_pos, rect, self._scroll_rect, pad_x=6, pad_y=0): - return f"toggle:{key}" + if _point_hits(mouse_pos, self._reset_rect, self._scroll_rect, pad_x=6, pad_y=0): + return "action:restore_defaults" return None def _activate_target(self, target: str): - if target.startswith("toggle:"): - key = target.split(":", 1)[1] - info = self._controller.ALERT_INFO.get(key) - if info and info.get("is_enabled", lambda: True)(): - current = self._controller._params.get_bool(key) - self._controller._params.put_bool(key, not current) + if target == "action:restore_defaults": + self._controller._restore_defaults() def show_event(self): super().show_event() @@ -165,9 +205,8 @@ class SoundsManagerView(Widget): def _render(self, rect: rl.Rectangle): self.set_rect(rect) - self._toggle_rects.clear() - frame, scroll_rect, content_width = init_list_panel(rect, PANEL_STYLE) + frame, scroll_rect, content_width = init_list_panel(rect, PANEL_STYLE, metrics=SOUNDS_PANEL_METRICS) self._scroll_rect = scroll_rect self._draw_header(frame.header) @@ -206,26 +245,50 @@ class SoundsManagerView(Widget): left_h = 0.0 for key in self._controller.VOLUME_KEYS: left_h += self._adjustor_rows[key].measure_height(col_width) - right_h = self._adjustor_rows[self._controller.COOLDOWN_KEY].measure_height(col_width) - right_h += AETHER_LIST_METRICS.utility_row_height * len(self._controller.CUSTOM_ALERTS_KEYS) - return max(left_h, right_h) + left_h += GROUP_HEADER_HEIGHT + GROUP_HEADER_GAP + left_h += GROUP_HEADER_HEIGHT + GROUP_HEADER_GAP + left_h += GROUP_HEADER_HEIGHT + GROUP_HEADER_GAP + + cd_h = self._adjustor_rows[self._controller.COOLDOWN_KEY].measure_height(col_width) + group_h = GROUP_HEADER_HEIGHT + GROUP_HEADER_GAP + self._tile_grid_h = left_h - cd_h - group_h + + right_h = cd_h + group_h + self._tile_grid_h + + section_overhead = SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP + return section_overhead + max(left_h, right_h) + 12.0 def _draw_header(self, rect: rl.Rectangle): draw_settings_panel_header(rect, tr("Sounds & Alerts"), tr("Manage system volumes and custom alert toggles."), subtitle_size=24) + btn_w = 120.0 + btn_h = 38.0 + btn_x = rect.x + rect.width - btn_w + btn_y = rect.y + (rect.height - btn_h) / 2 + self._reset_rect = rl.Rectangle(btn_x, btn_y, btn_w, btn_h) + + hovered = _point_hits(gui_app.last_mouse_event.pos, self._reset_rect, self._scroll_rect, pad_x=6, pad_y=0) + pressed = self._pressed_target == "action:restore_defaults" + draw_action_pill( + self._reset_rect, tr("Reset All"), + rl.Color(255, 255, 255, 8 if not pressed else 14), + rl.Color(255, 255, 255, 18 if not pressed else 28), + AetherListColors.SUBTEXT if not hovered else AetherListColors.HEADER, + ) + def _draw_scroll_content(self, rect: rl.Rectangle, content_width: float): y = rect.y + self._scroll_offset col_width = (content_width - SECTION_GAP) / 2 draw_section_header( - rl.Rectangle(rect.x, y, col_width, AETHER_LIST_METRICS.section_header_height), + rl.Rectangle(rect.x, y, col_width, SECTION_HEADER_HEIGHT), tr("Volume"), style=PANEL_STYLE ) draw_section_header( - rl.Rectangle(rect.x + col_width + SECTION_GAP, y, col_width, AETHER_LIST_METRICS.section_header_height), + rl.Rectangle(rect.x + col_width + SECTION_GAP, y, col_width, SECTION_HEADER_HEIGHT), tr("Alerts"), style=PANEL_STYLE ) - y += AETHER_LIST_METRICS.section_header_height + AETHER_LIST_METRICS.section_header_gap + y += SECTION_HEADER_HEIGHT + SECTION_HEADER_GAP col_left = rl.Rectangle(rect.x, y, col_width, self._content_height) col_right = rl.Rectangle(rect.x + col_width + SECTION_GAP, y, col_width, self._content_height) @@ -233,16 +296,40 @@ class SoundsManagerView(Widget): self._draw_volume_column(col_left) self._draw_utility_column(col_right) + def _draw_group_header(self, x: float, y: float, width: float, label: str) -> float: + gui_label(rl.Rectangle(x, y, width, GROUP_HEADER_HEIGHT), label, 14, GROUP_HEADER_COLOR, FontWeight.MEDIUM) + y += GROUP_HEADER_HEIGHT + rl.draw_line(int(x), int(y), int(x + width), int(y), GROUP_HAIRLINE_COLOR) + return y + GROUP_HEADER_GAP + def _draw_volume_column(self, rect: rl.Rectangle): + safety_keys = ["WarningImmediateVolume", "WarningSoftVolume", "RefuseVolume", "PromptDistractedVolume"] + system_keys = ["EngageVolume", "DisengageVolume"] + info_keys = ["PromptVolume", "BelowSteerSpeedVolume"] + + groups = [ + (tr("SAFETY ALERTS"), safety_keys), + (tr("SYSTEM STATE"), system_keys), + (tr("INFORMATIONAL"), info_keys), + ] + + total_h = sum(GROUP_HEADER_HEIGHT + GROUP_HEADER_GAP + sum(self._adjustor_rows[k].measure_height(rect.width) for k in keys) for _, keys in groups) + draw_list_group_shell( + rl.Rectangle(rect.x - 12, rect.y - 12, rect.width + 24, total_h + 24), + style=PANEL_STYLE + ) + current_y = rect.y - for key in self._controller.VOLUME_KEYS: - adjustor = self._adjustor_rows[key] - row_h = adjustor.measure_height(rect.width) - row_rect = rl.Rectangle(rect.x, current_y, rect.width, row_h) - adjustor.set_is_last(True) - adjustor.set_parent_rect(self._scroll_rect) - adjustor.render(row_rect) - current_y += row_h + for label, keys in groups: + current_y = self._draw_group_header(rect.x, current_y, rect.width, label) + for key in keys: + adjustor = self._adjustor_rows[key] + row_h = adjustor.measure_height(rect.width) + row_rect = rl.Rectangle(rect.x, current_y, rect.width, row_h) + adjustor.set_is_last(True) + adjustor.set_parent_rect(self._scroll_rect) + adjustor.render(row_rect) + current_y += row_h def _draw_utility_column(self, rect: rl.Rectangle): current_y = rect.y @@ -255,41 +342,28 @@ class SoundsManagerView(Widget): adjustor.render(rl.Rectangle(rect.x, current_y, rect.width, row_h)) current_y += row_h - toggle_h = AETHER_LIST_METRICS.utility_row_height - for i, key in enumerate(self._controller.CUSTOM_ALERTS_KEYS): - row_rect = rl.Rectangle(rect.x, current_y, rect.width, toggle_h) - self._draw_toggle_row(row_rect, key, self._controller.ALERT_INFO[key]) - current_y += toggle_h + current_y = self._draw_group_header(rect.x, current_y, rect.width, tr("CUSTOM ALERTS")) - def _draw_toggle_row(self, rect: rl.Rectangle, key: str, info: dict): - padded_rect = rl.Rectangle(rect.x, rect.y + 4, rect.width - 12, rect.height - 8) + tile_grid_h = self._tile_grid_h - current_val = self._controller._params.get_bool(key) - is_enabled = info.get("is_enabled", lambda: True)() + tile_grid_rect = rl.Rectangle(rect.x - 12, current_y - 12, rect.width + 24, tile_grid_h + 24) + draw_list_group_shell(tile_grid_rect, style=PANEL_STYLE) - mouse_pos = gui_app.last_mouse_event.pos - hovered = _point_hits(mouse_pos, padded_rect, self._scroll_rect, pad_x=6, pad_y=0) - pressed = self._pressed_target == f"toggle:{key}" - - status_str = tr("ON") if current_val else tr("OFF") - if not is_enabled: status_str = tr(info.get("disabled_label", "UNAVAILABLE")) - - draw_toggle_pill(padded_rect, current_val, is_enabled, tr(info["title"]), status_str, hovered, pressed, style=PANEL_STYLE) - - self._toggle_rects[key] = padded_rect + self._toggle_grid.set_parent_rect(self._scroll_rect) + self._toggle_grid.render(rl.Rectangle(rect.x, current_y, rect.width, tile_grid_h)) class StarPilotSoundsLayout(_SettingsPage): COOLDOWN_KEY = "SwitchbackModeCooldown" VOLUME_KEYS = [ - "BelowSteerSpeedVolume", - "DisengageVolume", - "EngageVolume", - "PromptVolume", - "PromptDistractedVolume", - "RefuseVolume", - "WarningSoftVolume", "WarningImmediateVolume", + "WarningSoftVolume", + "RefuseVolume", + "PromptDistractedVolume", + "EngageVolume", + "DisengageVolume", + "PromptVolume", + "BelowSteerSpeedVolume", ] CUSTOM_ALERTS_KEYS = [ "GreenLightAlert", @@ -298,16 +372,21 @@ class StarPilotSoundsLayout(_SettingsPage): "SpeedLimitChangedAlert", ] - COOLDOWN_INFO = {"title": tr_noop("Switchback Mode Cooldown"), "min": 0, "max": 30} + COOLDOWN_INFO = { + "title": tr_noop("Switchback Mode Cooldown"), + "subtitle": tr_noop("Time before switchback re-engages"), + "min": 0, + "max": 30, + } VOLUME_INFO = { - "BelowSteerSpeedVolume": {"title": tr_noop("Min Steer Speed Alert"), "min": 0}, - "DisengageVolume": {"title": tr_noop("Disengage Volume"), "min": 0}, - "EngageVolume": {"title": tr_noop("Engage Volume"), "min": 0}, - "PromptVolume": {"title": tr_noop("Prompt Volume"), "min": 0}, - "PromptDistractedVolume": {"title": tr_noop("Distracted Volume"), "min": 0}, - "RefuseVolume": {"title": tr_noop("Refuse Volume"), "min": 0}, - "WarningSoftVolume": {"title": tr_noop("Warning Soft"), "min": 25}, - "WarningImmediateVolume": {"title": tr_noop("Warning Immediate"), "min": 25}, + "WarningImmediateVolume": {"title": tr_noop("Immediate Warning"), "subtitle": tr_noop("Critical safety intervention"), "min": 25}, + "WarningSoftVolume": {"title": tr_noop("Soft Warning"), "subtitle": tr_noop("Approaching system limits"), "min": 25}, + "RefuseVolume": {"title": tr_noop("Engagement Refused"), "subtitle": tr_noop("Action refused by system"), "min": 0}, + "PromptDistractedVolume": {"title": tr_noop("Distracted Driver"), "subtitle": tr_noop("Driver attention required"), "min": 0}, + "EngageVolume": {"title": tr_noop("Engagement Chime"), "subtitle": tr_noop("System engaged confirmation"), "min": 0}, + "DisengageVolume": {"title": tr_noop("Disengagement Alert"), "subtitle": tr_noop("Handoff to manual control"), "min": 0}, + "PromptVolume": {"title": tr_noop("General Prompt"), "subtitle": tr_noop("System guidance prompt"), "min": 0}, + "BelowSteerSpeedVolume": {"title": tr_noop("Low Speed Alert"), "subtitle": tr_noop("Minimum speed for steering"), "min": 0}, } _sound_player_process = None @@ -317,15 +396,23 @@ class StarPilotSoundsLayout(_SettingsPage): self._init_sound_player() self.ALERT_INFO = { - "GreenLightAlert": {"title": tr_noop("Green Light")}, - "LeadDepartingAlert": {"title": tr_noop("Lead Departure")}, + "GreenLightAlert": { + "title": tr_noop("Green Light"), + "subtitle": tr_noop("When lead car moves at green light"), + }, + "LeadDepartingAlert": { + "title": tr_noop("Lead Departure"), + "subtitle": tr_noop("When lead vehicle pulls away"), + }, "LoudBlindspotAlert": { "title": tr_noop("Loud Blindspot"), + "subtitle": tr_noop("Blind spot collision warning"), "is_enabled": lambda: starpilot_state.car_state.hasBSM, "disabled_label": tr_noop("Needs BSM") }, "SpeedLimitChangedAlert": { "title": tr_noop("Speed Limit"), + "subtitle": tr_noop("When posted speed limit changes"), "is_enabled": lambda: self._params.get_bool("ShowSpeedLimits") or ( starpilot_state.car_state.hasOpenpilotLongitudinal and self._params.get_bool("SpeedLimitController") ), @@ -335,6 +422,14 @@ class StarPilotSoundsLayout(_SettingsPage): self._manager_view = SoundsManagerView(self) + def _restore_defaults(self): + for key in self.VOLUME_KEYS: + default = 25 if "Warning" in key else 100 + self._params.put_int(key, default) + self._params.put_int(self.COOLDOWN_KEY, 0) + for key in self.CUSTOM_ALERTS_KEYS: + self._params.put_bool(key, False) + @classmethod def _init_sound_player(cls): if cls._sound_player_process is not None and cls._sound_player_process.poll() is None: return