Compare commits

...

5 Commits

Author SHA1 Message Date
Jason Wen cbd0c6721a add branch end-of-life notice and onroad gate 2026-11-12 00:38:31 -05:00
royjr 43dac61e64 Update opendbc_repo 2026-04-19 12:06:08 -04:00
royjr 57245fbb29 Update opendbc_repo 2026-04-19 00:21:02 -04:00
royjr a88abfda2f Merge branch 'master' into hkg-angle-steering-2025-hda1 2026-04-18 23:47:01 -04:00
royjr 956bbcf3c0 Update opendbc_repo 2026-04-17 23:17:25 -04:00
8 changed files with 134 additions and 1 deletions
+2
View File
@@ -141,6 +141,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"BlinkerLateralReengageDelay", {PERSISTENT | BACKUP, INT, "0"}}, // seconds
{"BlinkerMinLateralControlSpeed", {PERSISTENT | BACKUP, INT, "20"}}, // MPH or km/h
{"BlinkerPauseLateralControl", {PERSISTENT | BACKUP, INT, "0"}},
{"BranchEolAck", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"Brightness", {PERSISTENT | BACKUP, INT, "0"}},
{"CarList", {PERSISTENT, JSON}},
{"CarParamsSP", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}},
@@ -169,6 +170,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"MaxTimeOffroad", {PERSISTENT | BACKUP, INT, "1800"}},
{"ModelRunnerTypeCache", {CLEAR_ON_ONROAD_TRANSITION, INT}},
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
{"Offroad_BranchEol", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
{"OnroadScreenOffBrightnessMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
+4
View File
@@ -1,4 +1,8 @@
{
"Offroad_BranchEol": {
"text": "This branch will not receive any more functional updates. Switch to hkg-angle-steering-2025. See the angle steering topic on community.sunnypilot.ai for what you need to do.",
"severity": 1
},
"Offroad_TemperatureTooHigh": {
"text": "Device temperature too high. System cooling down before starting. Current internal component temperature: %1",
"severity": 0
+3
View File
@@ -9,6 +9,7 @@ from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView
from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.system.ui.widgets import Widget
from openpilot.selfdrive.ui.layouts.onboarding import OnboardingWindow
from openpilot.selfdrive.ui.sunnypilot.layouts.branch_eol import BranchEolGate, BranchEolNotice
if gui_app.sunnypilot_ui():
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.settings import SettingsLayoutSP as SettingsLayout
@@ -46,6 +47,8 @@ class MainLayout(Widget):
if not self._onboarding_window.completed:
gui_app.push_widget(self._onboarding_window)
self._branch_eol = BranchEolGate(self, BranchEolNotice)
def _render(self, _):
self._handle_onroad_transition()
self._render_main_content()
+4
View File
@@ -6,6 +6,8 @@ from openpilot.selfdrive.ui.mici.layouts.offroad_alerts import MiciOffroadAlerts
from openpilot.selfdrive.ui.mici.onroad.augmented_road_view import AugmentedRoadView
from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.selfdrive.ui.mici.layouts.onboarding import OnboardingWindow
from openpilot.selfdrive.ui.sunnypilot.layouts.branch_eol import BranchEolGate
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.branch_eol import MiciBranchEolNotice
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.scroller import Scroller
from openpilot.system.ui.lib.application import gui_app
@@ -59,6 +61,8 @@ class MiciMainLayout(Scroller):
if not self._onboarding_window.completed:
gui_app.push_widget(self._onboarding_window)
self._branch_eol = BranchEolGate(self, MiciBranchEolNotice)
def _setup_callbacks(self):
self._home_layout.set_callbacks(
on_settings=lambda: gui_app.push_widget(self._settings_layout),
@@ -0,0 +1,85 @@
"""
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
import numpy as np
import pyray as rl
import qrcode
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import FontWeight, gui_app
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.button import Button, ButtonStyle
from openpilot.system.ui.widgets.label import Label
BRANCH_EOL_URL = "https://community.sunnypilot.ai/t/support-for-newer-vehicles-equipped-with-angle-steering-lfa2/22/751?u=sunnyhaibin"
BRANCH_EOL_TEXT = "hkg-angle-steering-2025-hda1 will not receive any more functional updates. Switch to hkg-angle-steering-2025."
class BranchEolGate:
# The ack param clears on boot and on every offroad transition, so the notice is shown again after each drive.
def __init__(self, main: Widget, notice_cls: Callable[[Callable[[], None]], Widget]):
self._main = main
self._notice = notice_cls(self._on_ack)
self._acked = False
gui_app.add_nav_stack_tick(self._tick)
def _tick(self):
if ui_state.started:
self._acked = False
elif not self._acked and gui_app.get_active_widget() is self._main:
gui_app.push_widget(self._notice)
def _on_ack(self):
self._acked = True
ui_state.params.put_bool("BranchEolAck", True)
gui_app.pop_widgets_to(self._main, instant=True)
class BranchEolNotice(Widget):
def __init__(self, on_ack: Callable[[], None]):
super().__init__()
self._title = Label(tr("This branch is no longer updated"), font_size=75, font_weight=FontWeight.BOLD,
text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT,
text_alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP)
self._desc = Label(tr(BRANCH_EOL_TEXT + "\n\nScan the QR code to see what you need to do."),
font_size=55, font_weight=FontWeight.MEDIUM,
text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT,
text_alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP)
self._ack_btn = Button(tr("I understand"), button_style=ButtonStyle.PRIMARY, click_callback=on_ack)
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4)
qr.add_data(BRANCH_EOL_URL)
qr.make(fit=True)
pil_img = qr.make_image(fill_color="black", back_color="white").convert('RGBA')
img_array = np.array(pil_img, dtype=np.uint8)
rl_image = rl.Image()
rl_image.data = rl.ffi.cast("void *", img_array.ctypes.data)
rl_image.width = pil_img.width
rl_image.height = pil_img.height
rl_image.mipmaps = 1
rl_image.format = rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
self._qr_texture = rl.load_texture_from_image(rl_image)
def _render(self, rect: rl.Rectangle):
rl.draw_rectangle_rec(rect, rl.BLACK)
qr_size = 560
qr_x = rect.x + rect.width - qr_size - 90
text_x = rect.x + 90
text_w = qr_x - text_x - 60
self._title.render(rl.Rectangle(text_x, rect.y + 90, text_w, 200))
self._desc.render(rl.Rectangle(text_x, rect.y + 300, text_w, 450))
src = rl.Rectangle(0, 0, self._qr_texture.width, self._qr_texture.height)
rl.draw_texture_pro(self._qr_texture, src, rl.Rectangle(qr_x, rect.y + 90, qr_size, qr_size), rl.Vector2(0, 0), 0, rl.WHITE)
self._ack_btn.render(rl.Rectangle(rect.x + 45, rect.y + rect.height - 160 - 45, rect.width - 90, 160))
def __del__(self):
if self._qr_texture.id != 0:
rl.unload_texture(self._qr_texture)
@@ -0,0 +1,33 @@
"""
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
import pyray as rl
from openpilot.selfdrive.ui.mici.layouts.onboarding import QRCodeWidget
from openpilot.selfdrive.ui.mici.widgets.dialog import BigConfirmationCircleButton
from openpilot.selfdrive.ui.sunnypilot.layouts.branch_eol import BRANCH_EOL_URL
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.mici_setup import GreyBigButton
from openpilot.system.ui.widgets.scroller import Scroller
class MiciBranchEolNotice(Scroller):
def __init__(self, on_ack: Callable[[], None]):
super().__init__()
self._scroller.add_widgets([
GreyBigButton("no longer\nupdated", "scroll to continue",
gui_app.texture("icons_mici/setup/green_info.png", 64, 64)),
GreyBigButton("", "This branch will not receive any more functional updates."),
GreyBigButton("switch to", "hkg-angle-steering-2025"),
GreyBigButton("scan the QR code", "to see what to do"),
QRCodeWidget(BRANCH_EOL_URL),
BigConfirmationCircleButton("I\nunderstand", gui_app.texture("icons_mici/setup/driver_monitoring/dm_check.png", 64, 64), on_ack),
])
def _render(self, _):
rl.draw_rectangle_rec(self._rect, rl.BLACK)
super()._render(_)
+2
View File
@@ -210,6 +210,7 @@ def hardware_thread(end_event, hw_queue) -> None:
thermal_config = HARDWARE.get_thermal_config()
fan_controller = FanController(int(1./DT_HW))
set_offroad_alert("Offroad_BranchEol", True)
while not end_event.is_set():
sm.update(PANDA_STATES_TIMEOUT)
@@ -301,6 +302,7 @@ def hardware_thread(end_event, hw_queue) -> None:
startup_conditions["up_to_date"] = params.get("Offroad_ConnectivityNeeded") is None or params.get_bool("DisableUpdates") or params.get_bool("SnoozeUpdate")
startup_conditions["no_excessive_actuation"] = params.get("Offroad_ExcessiveActuation") is None
startup_conditions["branch_eol_ack"] = params.get_bool("BranchEolAck")
startup_conditions["not_uninstalling"] = not params.get_bool("DoUninstall")
startup_conditions["accepted_terms"] = params.get("HasAcceptedTerms") == terms_version
startup_conditions["accepted_terms_sp"] = params.get("HasAcceptedTermsSP") == terms_version_sp