From 0bb2f8c9d43eed52afd4fac6c8531965d968b382 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 01:22:00 -0500 Subject: [PATCH 1/7] [bot] Update Python packages (#1707) Update Python packages Co-authored-by: github-actions[bot] --- opendbc_repo | 2 +- uv.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index a996ed701b..613a562bba 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit a996ed701bd0efe0ebcefa057373ba426dfb3a11 +Subproject commit 613a562bba80997b5c1c74cd799641d06fb2b38c diff --git a/uv.lock b/uv.lock index a6b43b6fe5..c2c8d3c8b6 100644 --- a/uv.lock +++ b/uv.lock @@ -414,11 +414,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.24.2" +version = "3.24.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/a8/dae62680be63cbb3ff87cfa2f51cf766269514ea5488479d42fec5aa6f3a/filelock-3.24.2.tar.gz", hash = "sha256:c22803117490f156e59fafce621f0550a7a853e2bbf4f87f112b11d469b6c81b", size = 37601, upload-time = "2026-02-16T02:50:45.614Z" } +sdist = { url = "https://files.pythonhosted.org/packages/73/92/a8e2479937ff39185d20dd6a851c1a63e55849e447a55e798cc2e1f49c65/filelock-3.24.3.tar.gz", hash = "sha256:011a5644dc937c22699943ebbfc46e969cdde3e171470a6e40b9533e5a72affa", size = 37935, upload-time = "2026-02-19T00:48:20.543Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/04/a94ebfb4eaaa08db56725a40de2887e95de4e8641b9e902c311bfa00aa39/filelock-3.24.2-py3-none-any.whl", hash = "sha256:667d7dc0b7d1e1064dd5f8f8e80bdac157a6482e8d2e02cd16fd3b6b33bd6556", size = 24152, upload-time = "2026-02-16T02:50:44Z" }, + { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331, upload-time = "2026-02-19T00:48:18.465Z" }, ] [[package]] From 5ccabb9d547d88794a3ee8808dfdf1bc88894aba Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 19 Feb 2026 01:36:08 -0500 Subject: [PATCH 2/7] [TIZI/TICI] ui: use `vCruiseCluster` and `vEgoCluster` for SLA `preActive` (#1708) --- selfdrive/ui/sunnypilot/onroad/speed_limit.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/sunnypilot/onroad/speed_limit.py b/selfdrive/ui/sunnypilot/onroad/speed_limit.py index a0b5ea3935..e8b5c5cfcd 100644 --- a/selfdrive/ui/sunnypilot/onroad/speed_limit.py +++ b/selfdrive/ui/sunnypilot/onroad/speed_limit.py @@ -22,6 +22,8 @@ from openpilot.system.ui.widgets import Widget METER_TO_FOOT = 3.28084 METER_TO_MILE = 0.000621371 AHEAD_THRESHOLD = 5 +SET_SPEED_NA = 255 +KM_TO_MILE = 0.621371 AssistState = custom.LongitudinalPlanSP.SpeedLimit.AssistState SpeedLimitSource = custom.LongitudinalPlanSP.SpeedLimit.Source @@ -58,8 +60,11 @@ class SpeedLimitRenderer(Widget): self.speed_limit_ahead_frame = 0 self.assist_frame = 0 - self.speed = 0.0 - self.set_speed = 0.0 + self.is_cruise_set: bool = False + self.is_cruise_available: bool = True + self.set_speed: float = SET_SPEED_NA + self.speed: float = 0.0 + self.v_ego_cluster_seen: bool = False self.font_bold = gui_app.font(FontWeight.BOLD) self.font_demi = gui_app.font(FontWeight.SEMI_BOLD) @@ -77,6 +82,8 @@ class SpeedLimitRenderer(Widget): def update(self): sm = ui_state.sm if sm.recv_frame["carState"] < ui_state.started_frame: + self.set_speed = SET_SPEED_NA + self.speed = 0.0 return if sm.updated["longitudinalPlanSP"]: @@ -106,9 +113,21 @@ class SpeedLimitRenderer(Widget): self.speed_limit_ahead_dist_prev = self.speed_limit_ahead_dist - cs = sm["carState"] - self.set_speed = cs.cruiseState.speed * self.speed_conv - v_ego = cs.vEgoCluster if cs.vEgoCluster != 0.0 else cs.vEgo + controls_state = sm['controlsState'] + car_state = sm["carState"] + + v_cruise_cluster = car_state.vCruiseCluster + self.set_speed = ( + controls_state.vCruiseDEPRECATED if v_cruise_cluster == 0.0 else v_cruise_cluster + ) + self.is_cruise_set = 0 < self.set_speed < SET_SPEED_NA + self.is_cruise_available = self.set_speed != -1 + + if self.is_cruise_set and not ui_state.is_metric: + self.set_speed *= KM_TO_MILE + + self.v_ego_cluster_seen = self.v_ego_cluster_seen or car_state.vEgoCluster != 0.0 + v_ego = car_state.vEgoCluster if self.v_ego_cluster_seen else car_state.vEgo self.speed = max(0.0, v_ego * self.speed_conv) @staticmethod From 082cf39d736ad10a10ca33c7ba3f01f12e0a9f00 Mon Sep 17 00:00:00 2001 From: Christopher Haucke <132518562+CHaucke89@users.noreply.github.com> Date: Sat, 21 Feb 2026 20:01:27 -0500 Subject: [PATCH 3/7] UI: Fix option control display for floating point params (#1711) --- system/ui/sunnypilot/widgets/option_control.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/ui/sunnypilot/widgets/option_control.py b/system/ui/sunnypilot/widgets/option_control.py index 91e9650ebd..291d8f6ff0 100644 --- a/system/ui/sunnypilot/widgets/option_control.py +++ b/system/ui/sunnypilot/widgets/option_control.py @@ -44,7 +44,8 @@ class OptionControlSP(ItemAction): self.current_value = int(key) break else: - self.current_value = int(self.params.get(self.param_key, return_default=True)) + value = self.params.get(self.param_key, return_default=True) + self.current_value = int(float(value) * 100.0) if self.use_float_scaling else int(value) # Initialize font and button styles self._font = gui_app.font(FontWeight.MEDIUM) From 111e8897be79f4c99734e77d0c389612f14a8e50 Mon Sep 17 00:00:00 2001 From: Nayan Date: Mon, 23 Feb 2026 22:23:08 -0500 Subject: [PATCH 4/7] models: fix default & index "0" (#1718) * default be gone * zero is the most significant number, don't ignore it. * thanks sentry!! --- common/params_keys.h | 2 +- sunnypilot/models/manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 6f27d9582d..8f3ee858f0 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -190,7 +190,7 @@ inline static std::unordered_map keys = { // Model Manager params {"ModelManager_ActiveBundle", {PERSISTENT, JSON}}, {"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}}, - {"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT, "0"}}, + {"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT}}, {"ModelManager_Favs", {PERSISTENT | BACKUP, STRING}}, {"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}}, {"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}}, diff --git a/sunnypilot/models/manager.py b/sunnypilot/models/manager.py index 2d3d670bfd..8fee0798b6 100644 --- a/sunnypilot/models/manager.py +++ b/sunnypilot/models/manager.py @@ -171,7 +171,7 @@ class ModelManagerSP: self.available_models = self.model_fetcher.get_available_bundles() self.active_bundle = get_active_bundle(self.params) - if index_to_download := self.params.get("ModelManager_DownloadIndex"): + if (index_to_download := self.params.get("ModelManager_DownloadIndex")) is not None: if model_to_download := next((model for model in self.available_models if model.index == index_to_download), None): try: self.download(model_to_download, Paths.model_root()) From 8a64cc57a96845c1ea67b537d648441631f97a6d Mon Sep 17 00:00:00 2001 From: Angus Dippenaar Date: Tue, 24 Feb 2026 06:22:40 +0100 Subject: [PATCH 5/7] [TIZI/TICI] visuals: Improved speed limit (#1713) improved speed limit Co-authored-by: royjr Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/onroad/speed_limit.py | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/selfdrive/ui/sunnypilot/onroad/speed_limit.py b/selfdrive/ui/sunnypilot/onroad/speed_limit.py index e8b5c5cfcd..39a9848d37 100644 --- a/selfdrive/ui/sunnypilot/onroad/speed_limit.py +++ b/selfdrive/ui/sunnypilot/onroad/speed_limit.py @@ -4,6 +4,7 @@ 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 dataclasses import dataclass import math import pyray as rl @@ -33,7 +34,7 @@ SpeedLimitSource = custom.LongitudinalPlanSP.SpeedLimit.Source class Colors: WHITE = rl.WHITE BLACK = rl.BLACK - RED = rl.RED + RED = rl.Color(235, 32, 32, 255) GREY = rl.Color(145, 155, 149, 255) DARK_GREY = rl.Color(77, 77, 77, 255) SUB_BG = rl.Color(0, 0, 0, 180) @@ -201,22 +202,17 @@ class SpeedLimitRenderer(Widget): center = rl.Vector2(rect.x + rect.width / 2, rect.y + rect.height / 2) radius = (rect.width + 18) / 2 - white = rl.Color(255, 255, 255, int(255 * alpha)) - red = rl.Color(255, 0, 0, int(255 * alpha)) - - if hasattr(color, 'r'): - text_color = rl.Color(color.r, color.g, color.b, int(255 * alpha)) - else: - text_color = rl.Color(color[0], color[1], color[2], int(255 * alpha)) - - black = rl.Color(0, 0, 0, int(255 * alpha)) - dark_grey = rl.Color(77, 77, 77, int(255 * alpha)) + white = rl.color_alpha(Colors.WHITE, alpha) + red = rl.color_alpha(Colors.RED, alpha) + black = rl.color_alpha(Colors.BLACK, alpha) + dark_grey = rl.color_alpha(Colors.DARK_GREY, alpha) + text_color = rl.color_alpha(color, alpha) rl.draw_circle_v(center, radius, white) - rl.draw_ring(center, radius * 0.80, radius, 0, 360, 36, red) + rl.draw_ring(center, radius * 0.75, radius, 0, 360, 36, red) - f_size = 70 if len(val) >= 3 else 85 - self._draw_text_centered(self.font_bold, val, f_size, center, text_color) + font_size = 70 if len(val) >= 3 else 85 + self._draw_text_centered(self.font_bold, val, font_size, center, text_color) if sub and has_limit: s_radius = radius * 0.4 @@ -225,22 +221,23 @@ class SpeedLimitRenderer(Widget): rl.draw_circle_v(s_center, s_radius, black) rl.draw_ring(s_center, s_radius - 3, s_radius, 0, 360, 36, dark_grey) - f_scale = 0.5 if len(sub) < 3 else 0.45 - self._draw_text_centered(self.font_bold, sub, int(s_radius * 2 * f_scale), s_center, white) + font_scale = 0.5 if len(sub) < 3 else 0.45 + self._draw_text_centered(self.font_bold, sub, int(s_radius * 2 * font_scale), s_center, white) def _render_mutcd(self, rect, val, sub, color, has_limit, alpha=1.0): - white = rl.Color(255, 255, 255, int(255 * alpha)) - black = rl.Color(0, 0, 0, int(255 * alpha)) - dark_grey = rl.Color(77, 77, 77, int(255 * alpha)) - - if hasattr(color, 'r'): - text_color = rl.Color(color.r, color.g, color.b, int(255 * alpha)) - else: - text_color = rl.Color(color[0], color[1], color[2], int(255 * alpha)) + white = rl.color_alpha(Colors.WHITE, alpha) + black = rl.color_alpha(Colors.BLACK, alpha) + dark_grey = rl.color_alpha(Colors.DARK_GREY, alpha) + text_color = rl.color_alpha(color, alpha) rl.draw_rectangle_rounded(rect, 0.35, 10, white) + inner = rl.Rectangle(rect.x + 10, rect.y + 10, rect.width - 20, rect.height - 20) - rl.draw_rectangle_rounded_lines_ex(inner, 0.35, 10, 4, black) + outer_radius = 0.35 * rect.width / 2.0 + inner_radius = outer_radius - 10.0 + inner_roundness = inner_radius / (inner.width / 2.0) + + rl.draw_rectangle_rounded_lines_ex(inner, inner_roundness, 10, 4, black) self._draw_text_centered(self.font_demi, "SPEED", 40, rl.Vector2(rect.x + rect.width / 2, rect.y + 40), black) self._draw_text_centered(self.font_demi, "LIMIT", 40, rl.Vector2(rect.x + rect.width / 2, rect.y + 80), black) From c48b724de1e3f44e0eaea43b1c2cf79c9b5ece1e Mon Sep 17 00:00:00 2001 From: James Mikesell Date: Tue, 24 Feb 2026 02:38:19 -0500 Subject: [PATCH 6/7] ICBM: ensure button timers update on disable to clear stale presses (#1688) * ICBM: ensure button timers update on disable to clear stale presses * fix race condition --------- Co-authored-by: Jason Wen --- selfdrive/car/cruise.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/cruise.py b/selfdrive/car/cruise.py index 9973862b85..572dfabc02 100644 --- a/selfdrive/car/cruise.py +++ b/selfdrive/car/cruise.py @@ -48,14 +48,14 @@ class VCruiseHelper(VCruiseHelperSP): self.get_minimum_set_speed(is_metric) + _enabled = self.update_enabled_state(CS, enabled) + if CS.cruiseState.available: - _enabled = self.update_enabled_state(CS, enabled) if not self.CP.pcmCruise or (not self.CP_SP.pcmCruiseSpeed and _enabled): # if stock cruise is completely disabled, then we can use our own set speed logic self._update_v_cruise_non_pcm(CS, _enabled, is_metric) self.update_speed_limit_assist_v_cruise_non_pcm() self.v_cruise_cluster_kph = self.v_cruise_kph - self.update_button_timers(CS, enabled) else: self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH @@ -69,6 +69,9 @@ class VCruiseHelper(VCruiseHelperSP): self.v_cruise_kph = V_CRUISE_UNSET self.v_cruise_cluster_kph = V_CRUISE_UNSET + if not self.CP.pcmCruise or not self.CP_SP.pcmCruiseSpeed: + self.update_button_timers(CS, enabled) + def _update_v_cruise_non_pcm(self, CS, enabled, is_metric): # handle button presses. TODO: this should be in state_control, but a decelCruise press # would have the effect of both enabling and changing speed is checked after the state transition From 03a14ff864af684f9f894a4f8b207e76e690c3b6 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 24 Feb 2026 03:02:36 -0500 Subject: [PATCH 7/7] [TIZI/TICI] ui: simplify Smart Cruise Control text rendering (#1719) --- .../sunnypilot/onroad/smart_cruise_control.py | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/selfdrive/ui/sunnypilot/onroad/smart_cruise_control.py b/selfdrive/ui/sunnypilot/onroad/smart_cruise_control.py index ca71fcac4a..b4848141e9 100644 --- a/selfdrive/ui/sunnypilot/onroad/smart_cruise_control.py +++ b/selfdrive/ui/sunnypilot/onroad/smart_cruise_control.py @@ -25,7 +25,6 @@ class SmartCruiseControlRenderer(Widget): self.long_override = False self.font = gui_app.font(FontWeight.BOLD) - self.scc_tex = rl.load_render_texture(256, 128) def update(self): sm = ui_state.sm @@ -65,44 +64,24 @@ class SmartCruiseControlRenderer(Widget): sz = measure_text_cached(self.font, text, font_size) box_height = int(sz.y + padding_v * 2) - texture_width = 256 - texture_height = 128 - - rl.begin_texture_mode(self.scc_tex) - rl.clear_background(rl.Color(0, 0, 0, 0)) - if self.long_override: box_color = COLORS.OVERRIDE else: box_color = rl.Color(0, 255, 0, 255) - # Center box in texture - box_x = (texture_width - box_width) // 2 - box_y = (texture_height - box_height) // 2 + screen_y = rect_height / 4 + y_offset + box_x = rect_center_x + x_offset - box_width / 2 + box_y = screen_y - box_height / 2 + + # Draw rounded background box rl.draw_rectangle_rounded(rl.Rectangle(box_x, box_y, box_width, box_height), 0.2, 10, box_color) - # Draw text with custom blend mode to punch hole - rl.rl_set_blend_factors(rl.RL_ZERO, rl.RL_ONE_MINUS_SRC_ALPHA, 0x8006) - rl.rl_set_blend_mode(rl.BLEND_CUSTOM) - + # Draw text centered in the box (black color for contrast against bright green/grey) text_pos_x = box_x + (box_width - sz.x) / 2 text_pos_y = box_y + (box_height - sz.y) / 2 - rl.draw_text_ex(self.font, text, rl.Vector2(text_pos_x, text_pos_y), font_size, 0, rl.WHITE) - - rl.rl_set_blend_mode(rl.BLEND_ALPHA) # Reset - rl.end_texture_mode() - - screen_y = rect_height / 4 + y_offset - - dest_x = rect_center_x + x_offset - texture_width / 2 - dest_y = screen_y - texture_height / 2 - - src_rect = rl.Rectangle(0, 0, texture_width, -texture_height) - dst_rect = rl.Rectangle(dest_x, dest_y, texture_width, texture_height) - - rl.draw_texture_pro(self.scc_tex.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0, rl.WHITE) + rl.draw_text_ex(self.font, text, rl.Vector2(text_pos_x, text_pos_y), font_size, 0, rl.BLACK) def _render(self, rect: rl.Rectangle): x_offset = -260