From 2e82908c0788e5613d8a7cea943934edcb32eeab Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 10 Mar 2026 20:30:25 -0400 Subject: [PATCH 1/4] pandad: always prioritize internal panda (#1759) * pandad: filter out external panda * fix * internal panda * move it even higher * this * should be this still * anoter * more * 1 more time * bruh * try this out * revert * gotta do this after * filter --- selfdrive/pandad/pandad.py | 26 +++++++++---------- .../selfdrive/pandad/rivian_long_flasher.py | 4 +++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index a8300de32..f65c64259 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -68,21 +68,20 @@ def flash_panda(panda_serial: str) -> Panda: return panda -def check_panda_support(panda_serials: list[str]) -> bool: - unsupported = [] +def check_panda_support(panda_serials: list[str]) -> list[str]: + spi_serials = set(Panda.spi_list()) + for serial in panda_serials: + if serial in spi_serials: + return [serial] + for serial in panda_serials: panda = Panda(serial) - hw_type = panda.get_type() + is_internal = panda.is_internal() panda.close() - if hw_type in Panda.SUPPORTED_DEVICES: - return True + if is_internal: + return [serial] - unsupported.append((serial, hw_type)) - - for serial, hw_type in unsupported: - cloudlog.warning(f"Panda {serial} is not supported (hw_type: {hw_type}), skipping...") - - return False + return [] def main() -> None: @@ -137,8 +136,9 @@ def main() -> None: # custom flasher for xnor's Rivian Longitudinal Upgrade Kit flash_rivian_long(panda_serials) - # skip flashing and health check if no supported panda is detected - if not check_panda_support(panda_serials): + # find the internal supported panda (e.g. skip external Black Panda) + panda_serials = check_panda_support(panda_serials) + if len(panda_serials) == 0: continue # Flash the first panda diff --git a/sunnypilot/selfdrive/pandad/rivian_long_flasher.py b/sunnypilot/selfdrive/pandad/rivian_long_flasher.py index 70ddb4435..305b994c7 100755 --- a/sunnypilot/selfdrive/pandad/rivian_long_flasher.py +++ b/sunnypilot/selfdrive/pandad/rivian_long_flasher.py @@ -84,7 +84,11 @@ def flash_rivian_long(panda_serials: list[str]) -> None: cloudlog.info("Not a Rivian, skipping longitudinal upgrade...") return + # only check USB connected pandas, internal panda uses SPI and is never an external panda + usb_serials = set(Panda.usb_list()) for serial in panda_serials: + if serial not in usb_serials: + continue panda = Panda(serial) # only flash external black pandas (HW_TYPE_BLACK = 0x03) if panda.get_type() == b'\x03' and not panda.is_internal(): From 0376660023fe0a141d1429e9dce7cdadf9f5e5c0 Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Fri, 13 Mar 2026 10:19:45 -0700 Subject: [PATCH 2/4] ci: modify models repo title (#1764) --- .github/workflows/build-all-tinygrad-models.yaml | 6 +++--- .github/workflows/build-single-tinygrad-model.yaml | 2 +- sunnypilot/models/fetcher.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-all-tinygrad-models.yaml b/.github/workflows/build-all-tinygrad-models.yaml index e901baee0..412676e5f 100644 --- a/.github/workflows/build-all-tinygrad-models.yaml +++ b/.github/workflows/build-all-tinygrad-models.yaml @@ -34,10 +34,10 @@ jobs: echo "tinygrad_ref=$ref" >> $GITHUB_OUTPUT echo "tinygrad_ref is $ref" - - name: Checkout docs repo (sunnypilot-docs, gh-pages) + - name: Checkout docs repo (sunnypilot-models, gh-pages) uses: actions/checkout@v4 with: - repository: sunnypilot/sunnypilot-docs + repository: sunnypilot/sunnypilot-models ref: gh-pages path: docs ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }} @@ -202,7 +202,7 @@ jobs: - name: Checkout docs repo uses: actions/checkout@v4 with: - repository: sunnypilot/sunnypilot-docs + repository: sunnypilot/sunnypilot-models ref: gh-pages path: docs ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }} diff --git a/.github/workflows/build-single-tinygrad-model.yaml b/.github/workflows/build-single-tinygrad-model.yaml index fae9d6aa0..e7e3b67b5 100644 --- a/.github/workflows/build-single-tinygrad-model.yaml +++ b/.github/workflows/build-single-tinygrad-model.yaml @@ -119,7 +119,7 @@ jobs: - name: Checkout docs repo uses: actions/checkout@v4 with: - repository: sunnypilot/sunnypilot-docs + repository: sunnypilot/sunnypilot-models ref: gh-pages path: docs ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }} diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py index 5990ee2e4..452c59e06 100644 --- a/sunnypilot/models/fetcher.py +++ b/sunnypilot/models/fetcher.py @@ -116,7 +116,7 @@ class ModelCache: class ModelFetcher: """Handles fetching and caching of model data from remote source""" - MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-docs/refs/heads/gh-pages/docs/driving_models_v15.json" + MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_v15.json" def __init__(self, params: Params): self.params = params From 37ac33fbcccb17e7beb3b329de6c19aa5803c42f Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 13 Mar 2026 19:19:37 -0400 Subject: [PATCH 3/4] gitignore: add CLAUDE.md and SKILL.md --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cd5e64e52..ebe6ca6c9 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,8 @@ Pipfile .context/ PLAN.md TASK.md +CLAUDE.md +SKILL.md ### JetBrains ### !.idea/customTargets.xml From 23c774eb19fd7e5c00cc65f45e6a97f58fca2564 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 17 Mar 2026 06:21:03 -0400 Subject: [PATCH 4/4] sunnylinkd: fetch compressed params schema (#1771) --- sunnypilot/sunnylink/athena/sunnylinkd.py | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/sunnypilot/sunnylink/athena/sunnylinkd.py b/sunnypilot/sunnylink/athena/sunnylinkd.py index e8066dca4..39fe5679c 100755 --- a/sunnypilot/sunnylink/athena/sunnylinkd.py +++ b/sunnypilot/sunnylink/athena/sunnylinkd.py @@ -229,6 +229,41 @@ def getParamsAllKeysV1() -> dict[str, str]: raise +@dispatcher.add_method +def getParamsMetadata() -> str: + """Compressed equivalent of getParamsAllKeysV1 — same struct, gzipped + base64.""" + try: + with open(METADATA_PATH) as f: + metadata = json.load(f) + except Exception: + cloudlog.exception("sunnylinkd.getParamsMetadata.exception") + metadata = {} + + try: + available_keys: list[str] = [k.decode('utf-8') for k in Params().all_keys()] + + params_list: list[dict] = [] + for key in available_keys: + value = get_param_as_byte(key, get_default=True) + + param_entry: dict = { + "key": key, + "type": int(params.get_type(key).value), + "default_value": base64.b64encode(value).decode('utf-8') if value else None, + } + + if key in metadata: + param_entry["_extra"] = metadata[key] + + params_list.append(param_entry) + + raw = json.dumps(params_list, separators=(',', ':')).encode('utf-8') + return base64.b64encode(gzip.compress(raw)).decode('utf-8') + except Exception: + cloudlog.exception("sunnylinkd.getParamsMetadata.exception") + raise + + @dispatcher.add_method def getParams(params_keys: list[str], compression: bool = False) -> str | dict[str, str]: params = Params()