From 7bd6cad821890db71c6ebacc2ccc78c2e662a948 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 18 Aug 2026 19:06:48 -0700 Subject: [PATCH 01/11] Re-open agnos updater UI if crash (#38672) loop if crash --- launch_chffrplus.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 62ebffd27..f30e03ca6 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -24,7 +24,9 @@ function agnos_init { if $AGNOS_PY --verify $MANIFEST; then sudo reboot fi - $DIR/openpilot/common/hardware/comma/updater $AGNOS_PY $MANIFEST + while true; do + $DIR/openpilot/common/hardware/comma/updater $AGNOS_PY $MANIFEST + done fi } From 20fdc3d824d86b62d83ea5c1b857b031feee98b6 Mon Sep 17 00:00:00 2001 From: stef <19478336+stefpi@users.noreply.github.com> Date: Wed, 19 Aug 2026 00:13:57 -0400 Subject: [PATCH 02/11] webrtcd: cloud logging (#38674) * logging * remove test * get rid of redudant try except * fix logger context --- openpilot/system/athena/athenad.py | 8 ++++++-- openpilot/system/webrtc/webrtcd.py | 26 +++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/openpilot/system/athena/athenad.py b/openpilot/system/athena/athenad.py index 828ae6b84..1351f45c2 100755 --- a/openpilot/system/athena/athenad.py +++ b/openpilot/system/athena/athenad.py @@ -795,7 +795,7 @@ def startStream(sdp: str, enabled: bool) -> dict: bridge_services_in = [] # stale car params case taken care of by webrtcd being shut off on ignition - cp_bytes = Params().get("CarParamsPersistent") + cp_bytes = params.get("CarParamsPersistent") if cp_bytes is not None: with car.CarParams.from_bytes(cp_bytes) as CP: if CP.notCar: @@ -808,7 +808,11 @@ def startStream(sdp: str, enabled: bool) -> dict: # webrtcd clears IsLiveStreaming when the session ends params.put_bool("IsLiveStreaming", True) # wait for webrtcd end points to wake up - wait_for_webrtcd() + try: + wait_for_webrtcd() + except TimeoutError: + cloudlog.event("athena.startStream.webrtcd_offroad_start_timeout", error=True) + raise return post_stream_request(StreamRequestBody(sdp, ["wideRoad"], enabled, bridge_services_in, ["carState", "deviceState"])) diff --git a/openpilot/system/webrtc/webrtcd.py b/openpilot/system/webrtc/webrtcd.py index 3c0c3037f..56b4ac43c 100755 --- a/openpilot/system/webrtc/webrtcd.py +++ b/openpilot/system/webrtc/webrtcd.py @@ -21,10 +21,16 @@ from typing import Any from openpilot.system.webrtc.helpers import StreamRequestBody from openpilot.system.webrtc.schema import generate_field from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog from openpilot.cereal import messaging, log SESSION_TIMEOUT_SECONDS = 300 + +# ice candidate parser for logging +def _ice_candidates(sdp: str) -> list[str]: + return [line.removeprefix("a=") for line in sdp.splitlines() if line.startswith("a=candidate:")] + # socket trick: route lookup for 8.8.8.8 (nothing is sent or actually connected to) # return the source interfaces IP which is the default interface of the device def _default_route_ip() -> str | None: @@ -253,7 +259,7 @@ class StreamSession: self._cleanup_lock = asyncio.Lock() self._cleanup_done = False self.logger = logging.getLogger("webrtcd") - self.logger.info( + cloudlog.warning( "New stream session (%s), video cameras %s, video enabled %s, incoming services %s, outgoing services %s", self.identifier, [t.id for t in self.video_tracks], body.enabled, body.bridge_services_in, body.bridge_services_out, ) @@ -341,14 +347,18 @@ class StreamSession: if self.bitrate_controller is not None: self.bitrate_controller.start() - self.logger.info("Stream session (%s) connected", self.identifier) + with cloudlog.ctx(session_id=self.identifier): + cloudlog.warning("webrtcd.session.connected") if self.is_body: await self.run_body_session() else: await self.run_normal_session() - self.logger.info("Stream session (%s) ended", self.identifier) + with cloudlog.ctx(session_id=self.identifier): + cloudlog.warning("webrtcd.session.ended") except Exception: self.logger.exception("Stream session failure") + with cloudlog.ctx(session_id=self.identifier): + cloudlog.exception("webrtcd.session.exception") finally: await self.post_run_cleanup() @@ -422,15 +432,25 @@ async def handle_get_stream(state: ServerState, raw_body: bytes, content_type: s stream_dict[session.identifier] = session try: answer = await asyncio.wait_for(session.get_answer(), timeout=30) + cloudlog.event( + "webrtcd.session.ice_candidates", + session_id=session.identifier, + offer_candidates=_ice_candidates(body.sdp), + answer_candidates=_ice_candidates(answer.sdp), + ) except TimeoutError: await session.stop() stream_dict.pop(session.identifier, None) logging.getLogger("webrtcd").exception("Timed out creating stream answer") + with cloudlog.ctx(session_id=session.identifier): + cloudlog.warning("webrtcd.session.answer_timeout") raise except Exception: await session.stop() stream_dict.pop(session.identifier, None) logging.getLogger("webrtcd").exception("Failed to create stream answer") + with cloudlog.ctx(session_id=session.identifier): + cloudlog.exception("webrtcd.session.answer_exception") raise session.start() From 5b36799eec73ee9d630ccf8304c43dccf8fe7a28 Mon Sep 17 00:00:00 2001 From: stef <19478336+stefpi@users.noreply.github.com> Date: Wed, 19 Aug 2026 00:56:33 -0400 Subject: [PATCH 03/11] webrtc: fix message handler race (#38675) open message handler early --- openpilot/system/webrtc/webrtcd.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpilot/system/webrtc/webrtcd.py b/openpilot/system/webrtc/webrtcd.py index 56b4ac43c..9481e077a 100755 --- a/openpilot/system/webrtc/webrtcd.py +++ b/openpilot/system/webrtc/webrtcd.py @@ -335,9 +335,12 @@ class StreamSession: async def run(self): try: self.params.put("LivestreamRequestKeyframe", True) + + # avoid datachannel race by adding messange_handler immediately + self.stream.set_message_handler(self.message_handler) + await asyncio.wait_for(self.stream.wait_for_connection(), timeout=15) if self.stream.has_messaging_channel(): - self.stream.set_message_handler(self.message_handler) if self.incoming_bridge is not None: await self.shared_pub_master.add_services_if_needed(self.incoming_bridge_services) if self.outgoing_bridge is not None: From a8d1a280c665dcca7923782f8bfdf15c7831bdda Mon Sep 17 00:00:00 2001 From: stef <19478336+stefpi@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:49:19 -0400 Subject: [PATCH 04/11] webrtcd/athenad: we don't have to fail on no car params (#38678) we don't have to fail on no car params --- openpilot/system/athena/athenad.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpilot/system/athena/athenad.py b/openpilot/system/athena/athenad.py index 1351f45c2..2b6763877 100755 --- a/openpilot/system/athena/athenad.py +++ b/openpilot/system/athena/athenad.py @@ -800,8 +800,6 @@ def startStream(sdp: str, enabled: bool) -> dict: with car.CarParams.from_bytes(cp_bytes) as CP: if CP.notCar: bridge_services_in.append("testJoystick") - else: - raise Exception("failed to get CarParamsPersistent") if params.get_bool("IsOffroad"): # manager owns camerad/stream_encoderd/webrtcd; flip the param and let it bring them up. From dcf9d25bf36f335bc9d5b1618f20f0e53aa22238 Mon Sep 17 00:00:00 2001 From: stef <19478336+stefpi@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:02:03 -0400 Subject: [PATCH 05/11] webrtcd: more descriptive errors (#38677) more descriptive errors --- openpilot/system/webrtc/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpilot/system/webrtc/helpers.py b/openpilot/system/webrtc/helpers.py index de45e1c6c..87fe20eb2 100644 --- a/openpilot/system/webrtc/helpers.py +++ b/openpilot/system/webrtc/helpers.py @@ -23,9 +23,9 @@ def post_stream_request(body: StreamRequestBody) -> dict: ret["time"] = (t_end - t_start) * 1000 return ret except requests.ConnectTimeout as e: - raise Exception("webrtc took too long to respond.") from e + raise Exception("device took too long to respond.") from e except requests.ConnectionError as e: - raise Exception("webrtc server on device is not running.") from e + raise Exception("turn car ignition off to use livestreaming.") from e def wait_for_webrtcd(max_retries: float = 10) -> None: @@ -37,4 +37,4 @@ def wait_for_webrtcd(max_retries: float = 10) -> None: except requests.ConnectionError: attempts += 1 time.sleep(0.5) - raise TimeoutError("webrtcd did not initialize in time.") + raise TimeoutError("livestreaming service did not initialize in time.") From 555f48c5d28709f039b79f3f6105e51305edd4b5 Mon Sep 17 00:00:00 2001 From: stef <19478336+stefpi@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:21:59 -0400 Subject: [PATCH 06/11] params: remove livestream param on ignition (#38679) * remove livestream param on ignition * simplify process config --- openpilot/common/params_keys.h | 2 +- openpilot/system/manager/process_config.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openpilot/common/params_keys.h b/openpilot/common/params_keys.h index e44d8f8e3..2a49690b7 100644 --- a/openpilot/common/params_keys.h +++ b/openpilot/common/params_keys.h @@ -59,7 +59,7 @@ inline static std::unordered_map keys = { {"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsEngaged", {PERSISTENT, BOOL}}, {"IsLdwEnabled", {PERSISTENT, BOOL}}, - {"IsLiveStreaming", {CLEAR_ON_MANAGER_START, BOOL}}, + {"IsLiveStreaming", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}}, {"IsMetric", {PERSISTENT, BOOL}}, {"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsRhdDetected", {PERSISTENT, BOOL}}, diff --git a/openpilot/system/manager/process_config.py b/openpilot/system/manager/process_config.py index 32d850869..b8a1e4a12 100644 --- a/openpilot/system/manager/process_config.py +++ b/openpilot/system/manager/process_config.py @@ -67,15 +67,12 @@ def or_(*fns): def and_(*fns): return lambda *args: operator.and_(*(fn(*args) for fn in fns)) -def not_(*fns): - return lambda *args: operator.not_(*(fn(*args) for fn in fns)) - procs = [ DaemonProcess("manage_athenad", "openpilot.system.athena.manage_athenad", "AthenadPid"), NativeProcess("loggerd", "openpilot/system/loggerd", ["./loggerd"], logging), NativeProcess("encoderd", "openpilot/system/loggerd", ["./encoderd"], only_onroad), - NativeProcess("stream_encoderd", "openpilot/system/loggerd", ["./encoderd", "--stream"], or_(and_(livestream, not_(iscar)), notcar)), + NativeProcess("stream_encoderd", "openpilot/system/loggerd", ["./encoderd", "--stream"], or_(livestream, notcar)), PythonProcess("logmessaged", "openpilot.system.logmessaged", always_run), NativeProcess("camerad", "openpilot/system/camerad", ["./camerad"], or_(driverview, livestream), enabled=not WEBCAM), @@ -119,7 +116,7 @@ procs = [ # debug procs NativeProcess("bridge", "openpilot/cereal/messaging", ["./bridge"], notcar), - PythonProcess("webrtcd", "openpilot.system.webrtc.webrtcd", or_(and_(livestream, not_(iscar)), notcar)), + PythonProcess("webrtcd", "openpilot.system.webrtc.webrtcd", or_(livestream, notcar)), PythonProcess("joystick", "openpilot.tools.joystick.joystick_control", and_(joystick, iscar)), ] From 084747c75d2cbd23af65ab7a9e770bbd7b98bac9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 21 Aug 2026 15:41:12 -0700 Subject: [PATCH 07/11] Fix button label widths (#38680) * Revert "ui: fix text and icon overlap on button (#38628)" This reverts commit d9c4120f891430da60a353e91600483ef0292de1. * simple * can do this * fix eliding * Revert "fix eliding" This reverts commit b271a350182ad87f9942d7363383ee8ec72d0e36. * clean up * clean up --- openpilot/selfdrive/ui/mici/widgets/button.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/openpilot/selfdrive/ui/mici/widgets/button.py b/openpilot/selfdrive/ui/mici/widgets/button.py index cad40d7d0..e2912d00c 100644 --- a/openpilot/selfdrive/ui/mici/widgets/button.py +++ b/openpilot/selfdrive/ui/mici/widgets/button.py @@ -149,11 +149,15 @@ class BigButton(Widget): def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(lambda: touch_callback() and self._grow_animation_until is None) - def _width_hint(self) -> int: - # A value moves the title to the top, where it shares space with the icon. + def _title_width_hint(self) -> int: + # A value moves the title to the top, where it shares space with the icon icon_size = self._txt_icon.width if self._txt_icon and self.value else 0 return int(self._rect.width - self.LABEL_HORIZONTAL_PADDING * 2 - icon_size) + def _subtitle_width_hint(self) -> int: + # Bottom aligned, so it sits below the icon + return int(self._rect.width - self.LABEL_HORIZONTAL_PADDING * 2) + def _get_label_font_size(self): if len(self.text) <= 18: return 48 @@ -228,14 +232,14 @@ class BigButton(Widget): label_color = LABEL_COLOR if self.enabled else rl.Color(255, 255, 255, int(255 * 0.35)) self._label.set_color(label_color) - label_rect = rl.Rectangle(label_x, btn_y + self.LABEL_VERTICAL_PADDING, self._width_hint(), + label_rect = rl.Rectangle(label_x, btn_y + self.LABEL_VERTICAL_PADDING, self._title_width_hint(), self._rect.height - self.LABEL_VERTICAL_PADDING * 2) self._label.render(label_rect) if self.value: - label_y = btn_y + self.LABEL_VERTICAL_PADDING + self._label.get_content_height(self._width_hint()) + label_y = label_rect.y + self._label.get_content_height(int(label_rect.width)) sub_label_height = btn_y + self._rect.height - self.LABEL_VERTICAL_PADDING - label_y - sub_label_rect = rl.Rectangle(label_x, label_y, self._width_hint(), sub_label_height) + sub_label_rect = rl.Rectangle(label_x, label_y, self._subtitle_width_hint(), sub_label_height) self._sub_label.render(sub_label_rect) # ICON ------------------------------------------------------------------- @@ -312,9 +316,6 @@ class BigMultiToggle(BigToggle): self.set_value(self._options[0]) - def _width_hint(self) -> int: - return int(self._rect.width - self.LABEL_HORIZONTAL_PADDING * 2 - self._txt_enabled_toggle.width) - def _handle_mouse_release(self, mouse_pos: MousePos): super()._handle_mouse_release(mouse_pos) cur_idx = self._options.index(self.value) @@ -363,9 +364,6 @@ class GreyBigButton(BigButton): def LABEL_VERTICAL_PADDING(self): return BigButton.LABEL_VERTICAL_PADDING if self._label.text else 18 - def _width_hint(self) -> int: - return int(self._rect.width - self.LABEL_HORIZONTAL_PADDING * 2) - def _get_label_font_size(self): return 36 From 0de7fbf33d65b052efec8d5d1e2d810c3e5b48b2 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 23 Aug 2026 15:06:53 -0400 Subject: [PATCH 08/11] new --- openpilot/sunnypilot/models/default_model.py | 2 +- openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openpilot/sunnypilot/models/default_model.py b/openpilot/sunnypilot/models/default_model.py index 128426979..a8e55f8e6 100755 --- a/openpilot/sunnypilot/models/default_model.py +++ b/openpilot/sunnypilot/models/default_model.py @@ -9,7 +9,7 @@ from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL, DEFAULT_BIG_MO def get_default_model() -> str: - show_big_model = (ui_state.usbgpu and ui_state.usbgpu_compiled + show_big_model = (ui_state.usbgpu and (ui_state.usbgpu_active or ui_state.usbgpu_loading or ui_state.is_offroad())) return DEFAULT_BIG_MODEL if show_big_model else DEFAULT_MODEL diff --git a/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py b/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py index 27be6e24e..3425a8562 100755 --- a/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py +++ b/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py @@ -28,7 +28,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce create_connection, WebSocketConnectionClosedException) import openpilot.cereal.messaging as messaging -from openpilot.selfdrive.modeld.helpers import usbgpu_present, usbgpu_compiled +from openpilot.selfdrive.modeld.helpers import usbgpu_present from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL, DEFAULT_BIG_MODEL from openpilot.sunnypilot.selfdrive.car.sync_sunnylink_params import update_car_list_param from openpilot.sunnypilot.sunnylink.api import SunnylinkApi @@ -183,9 +183,10 @@ def getParamsMetadata() -> str: schema["capabilities"] = generate_capabilities() schema["capability_labels"] = CAPABILITY_LABELS # mirrors get_default_model() — ui_state unavailable in sunnylinkd process - show_big = (usbgpu_present() and usbgpu_compiled() + show_big = (usbgpu_present() and (params.get_bool("UsbGpuActive") or params.get_bool("UsbGpuLoading") or params.get_bool("IsOffroad"))) schema["default_model"] = DEFAULT_BIG_MODEL if show_big else DEFAULT_MODEL + schema["usbgpu_active"] = params.get_bool("UsbGpuActive") raw = json.dumps(schema, separators=(",", ":")).encode("utf-8") return base64.b64encode(gzip.compress(raw)).decode("utf-8") except Exception: From 699eaf79575754bb6c3cf8360f5c1e9d7026e669 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 23 Aug 2026 19:16:15 -0400 Subject: [PATCH 09/11] include them! --- .../selfdrive/ui/sunnypilot/mici/layouts/home.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py index d29e579c5..623002e8c 100644 --- a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py +++ b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py @@ -5,11 +5,22 @@ 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.home import MiciHomeLayout +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import FontWeight from openpilot.system.ui.widgets.label import UnifiedLabel +RUNNER_TINYGRAD = 1 + class MiciHomeLayoutSP(MiciHomeLayout): def __init__(self): super().__init__() self._openpilot_label = UnifiedLabel("sunnypilot", font_size=88, font_weight=FontWeight.AUDIOWIDE, max_width=480, wrap_text=False) + + def _render(self, rect): + super()._render(rect) + chestnut = ui_state.sm["deviceState"].chestnutPresent + if chestnut: + gpu_ready = ui_state.usbgpu_compiled or (ui_state.params.get("ModelRunnerTypeCache") == RUNNER_TINYGRAD) + self._egpu_icon.set_visible(gpu_ready) + self._egpu_icon_gray.set_visible(not gpu_ready) From dcddb2a0bdf4ca202f5925ff01a6acd0443c67a4 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 23 Aug 2026 19:53:46 -0400 Subject: [PATCH 10/11] models: revert icon override from this branch scope --- .../selfdrive/ui/sunnypilot/mici/layouts/home.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py index 623002e8c..d29e579c5 100644 --- a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py +++ b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py @@ -5,22 +5,11 @@ 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.home import MiciHomeLayout -from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import FontWeight from openpilot.system.ui.widgets.label import UnifiedLabel -RUNNER_TINYGRAD = 1 - class MiciHomeLayoutSP(MiciHomeLayout): def __init__(self): super().__init__() self._openpilot_label = UnifiedLabel("sunnypilot", font_size=88, font_weight=FontWeight.AUDIOWIDE, max_width=480, wrap_text=False) - - def _render(self, rect): - super()._render(rect) - chestnut = ui_state.sm["deviceState"].chestnutPresent - if chestnut: - gpu_ready = ui_state.usbgpu_compiled or (ui_state.params.get("ModelRunnerTypeCache") == RUNNER_TINYGRAD) - self._egpu_icon.set_visible(gpu_ready) - self._egpu_icon_gray.set_visible(not gpu_ready) From 94ed0608e6c62f33f7cf17aaa0498869e065324c Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 24 Aug 2026 01:40:31 -0400 Subject: [PATCH 11/11] models: use less strict chestnut detection state (#1948) --- openpilot/sunnypilot/models/fetcher.py | 17 ++++++++--------- openpilot/sunnypilot/models/manager.py | 4 +++- .../sunnypilot/sunnylink/athena/sunnylinkd.py | 7 ++----- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/openpilot/sunnypilot/models/fetcher.py b/openpilot/sunnypilot/models/fetcher.py index b64e27b1a..773eb5b95 100644 --- a/openpilot/sunnypilot/models/fetcher.py +++ b/openpilot/sunnypilot/models/fetcher.py @@ -13,8 +13,6 @@ from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.common.hardware.hw import Paths from openpilot.sunnypilot.models.helpers import is_bundle_version_compatible -from openpilot.selfdrive.modeld.helpers import usbgpu_present - from openpilot.cereal import custom @@ -149,11 +147,10 @@ class ModelFetcher: self._is_usbgpu: bool | None = None self.model_cache = ModelCache(params) self.model_url = self.MODEL_URL - self._update_model_source() - def _update_model_source(self) -> None: - """Updates what json to use based on usbgpu availability""" - is_usbgpu = usbgpu_present() + def _update_model_source(self, chestnut_present: bool) -> None: + """Updates what json to use based on chestnut hardware presence via deviceState""" + is_usbgpu = chestnut_present if is_usbgpu != self._is_usbgpu: self._is_usbgpu = is_usbgpu self.model_cache = ModelCache(self.params, suffix="_USBGPU" if is_usbgpu else "") @@ -191,9 +188,9 @@ class ModelFetcher: return None - def get_available_bundles(self) -> list[custom.ModelManagerSP.ModelBundle]: + def get_available_bundles(self, chestnut_present: bool = False) -> list[custom.ModelManagerSP.ModelBundle]: """Gets the list of available models, with smart cache handling""" - self._update_model_source() + self._update_model_source(chestnut_present) cached_data, is_expired = self.model_cache.get() if cached_data and not is_expired: @@ -210,10 +207,12 @@ class ModelFetcher: cloudlog.warning("Failed to fetch fresh data. Using expired cache as fallback") return self.model_parser.parse_models(cached_data) + if __name__ == "__main__": + from openpilot.selfdrive.modeld.helpers import usbgpu_present params = Params() model_fetcher = ModelFetcher(params) - bundles = model_fetcher.get_available_bundles() + bundles = model_fetcher.get_available_bundles(chestnut_present=usbgpu_present()) for bundle in bundles: for model in bundle.models: model_overrides = {override.key: override.value for override in bundle.overrides} diff --git a/openpilot/sunnypilot/models/manager.py b/openpilot/sunnypilot/models/manager.py index 8ca877587..37bcb781c 100644 --- a/openpilot/sunnypilot/models/manager.py +++ b/openpilot/sunnypilot/models/manager.py @@ -30,6 +30,7 @@ class ModelManagerSP: self.params = Params() self.model_fetcher = ModelFetcher(self.params) self.pm = messaging.PubMaster(["modelManagerSP"]) + self.sm = messaging.SubMaster(["deviceState"]) self.available_models: list[custom.ModelManagerSP.ModelBundle] = [] self.selected_bundle: custom.ModelManagerSP.ModelBundle = None self.active_bundle: custom.ModelManagerSP.ModelBundle = get_active_bundle(self.params) @@ -262,7 +263,8 @@ class ModelManagerSP: while True: try: - self.available_models = self.model_fetcher.get_available_bundles() + self.sm.update(0) + self.available_models = self.model_fetcher.get_available_bundles(self.sm['deviceState'].chestnutPresent) validate_active_bundle(self.params, self.available_models) self.active_bundle = get_active_bundle(self.params) diff --git a/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py b/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py index 3425a8562..1ab2f373e 100755 --- a/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py +++ b/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py @@ -28,7 +28,6 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce create_connection, WebSocketConnectionClosedException) import openpilot.cereal.messaging as messaging -from openpilot.selfdrive.modeld.helpers import usbgpu_present from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL, DEFAULT_BIG_MODEL from openpilot.sunnypilot.selfdrive.car.sync_sunnylink_params import update_car_list_param from openpilot.sunnypilot.sunnylink.api import SunnylinkApi @@ -182,10 +181,8 @@ def getParamsMetadata() -> str: schema = generate_schema() schema["capabilities"] = generate_capabilities() schema["capability_labels"] = CAPABILITY_LABELS - # mirrors get_default_model() — ui_state unavailable in sunnylinkd process - show_big = (usbgpu_present() - and (params.get_bool("UsbGpuActive") or params.get_bool("UsbGpuLoading") or params.get_bool("IsOffroad"))) - schema["default_model"] = DEFAULT_BIG_MODEL if show_big else DEFAULT_MODEL + schema["default_model"] = DEFAULT_MODEL + schema["default_big_model"] = DEFAULT_BIG_MODEL schema["usbgpu_active"] = params.get_bool("UsbGpuActive") raw = json.dumps(schema, separators=(",", ":")).encode("utf-8") return base64.b64encode(gzip.compress(raw)).decode("utf-8")