mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-22 03:42:07 +08:00
Compare commits
233 Commits
paramwatcher
...
tune
| Author | SHA1 | Date | |
|---|---|---|---|
| 319069378e | |||
| cedb501376 | |||
| fd1ec26e9a | |||
| 2b01fc4c9c | |||
| f81e7245fe | |||
| 0cb6b7b807 | |||
| 4a869778a9 | |||
| a120053d12 | |||
| a48988ccb3 | |||
| 0650964559 | |||
| b62014eb12 | |||
| 037e8c18f8 | |||
| 4bd60cd3cd | |||
| 3fe33cbe97 | |||
| b3878fb211 | |||
| 33a26ba373 | |||
| 58af294ffd | |||
| bafbfe19b4 | |||
| dae95af1df | |||
| 86d52ab969 | |||
| 52fb0b8171 | |||
| 5b98ea04ad | |||
| b9344af9bb | |||
| 1e0f1a8abc | |||
| 8ba36b76a0 | |||
| 3f382d6e69 | |||
| 10edb90ac6 | |||
| 45099e7fcd | |||
| 77f069cbe5 | |||
| 1070dda3ee | |||
| decd7d4c1c | |||
| fcd5897650 | |||
| dc5dfe7f3b | |||
| f9c57ff285 | |||
| f1785c245a | |||
| 6892b62761 | |||
| 3d3a5de409 | |||
| bef93ecf65 | |||
| a18ddf12eb | |||
| 46ae67b607 | |||
| 4d3aeaba6d | |||
| ba67e468ab | |||
| e946e9de0b | |||
| 7d2563880a | |||
| 43edc51cb6 | |||
| 9476a8a7f6 | |||
| 053441fbb3 | |||
| 1179273217 | |||
| e35a1eca99 | |||
| 3d11e8ef36 | |||
| c51b41e96a | |||
| 73f720220b | |||
| a1202eaf2a | |||
| a941e8f78f | |||
| 9aca13cf2d | |||
| 7722d14b89 | |||
| 043eab5620 | |||
| 51d3f0dea2 | |||
| 8be8bbbfdb | |||
| 1f778c8c23 | |||
| 981494a354 | |||
| 254f55ac15 | |||
| 96b58024ab | |||
| a9229e11a0 | |||
| 020f503364 | |||
| c274dba36e | |||
| 35c87a1519 | |||
| ac087d085e | |||
| 46d65095af | |||
| 81bd8aa0e2 | |||
| 667f3bb32f | |||
| c908189e73 | |||
| c65cf18c75 | |||
| 55a31d7657 | |||
| ac17c35cfe | |||
| bcb13a7229 | |||
| 0ce28803ec | |||
| db8cd9f411 | |||
| 51312afd3d | |||
| efc23644c7 | |||
| e531f844f6 | |||
| 46f74753cd | |||
| d5cbb89d84 | |||
| 4ce701150a | |||
| 96fded0399 | |||
| 12597856da | |||
| 593c3a0c8e | |||
| 187d3a079c | |||
| 9755520b87 | |||
| 7099bd18e3 | |||
| cb670c2c3d | |||
| f5b84e74f4 | |||
| 2b8e887e44 | |||
| 64f74dad27 | |||
| 7e959c5a3e | |||
| 28795d3065 | |||
| 8aed5a1a89 | |||
| 4d65c52e6d | |||
| a5c973be89 | |||
| 39dcc88330 | |||
| fc6afd5ab8 | |||
| 177a1a3c8b | |||
| 1979a6d8e8 | |||
| 944c23f369 | |||
| 2230933d4b | |||
| 3ea474dd58 | |||
| 8879d481e5 | |||
| 5e35feb3ab | |||
| 5c12a7cfc3 | |||
| f309be9038 | |||
| 959ebd22d8 | |||
| 5b6436a90c | |||
| ee7601ae9d | |||
| 54cf8d6a5e | |||
| aac90dd11b | |||
| 85b9f8962e | |||
| 8f970bcb99 | |||
| a668bc9eda | |||
| fd50941cff | |||
| 831f2396d9 | |||
| 5fc4c2b25c | |||
| b03e7821d4 | |||
| 35241a5fb8 | |||
| 5da6bf9e03 | |||
| 948d42b3e5 | |||
| 422de59898 | |||
| 7a990b99f7 | |||
| 0a84b00406 | |||
| e76e1e500c | |||
| cd70e23dc3 | |||
| 1dfef69a3c | |||
| c35df583a5 | |||
| db3df61c34 | |||
| 32f0a2cbbc | |||
| 569099eb70 | |||
| df7f426405 | |||
| bddd20c425 | |||
| e89e4407c5 | |||
| d849d6f1d7 | |||
| 0b958f7c9a | |||
| 2fc10e8299 | |||
| bf8cae5e7c | |||
| 93015c1c17 | |||
| 97329e46ae | |||
| d76f756f42 | |||
| 71a418d166 | |||
| fb58e8f1f7 | |||
| 5dea009113 | |||
| de024fd4a7 | |||
| 7c90c0669a | |||
| fc4a0fb944 | |||
| 5c01365125 | |||
| d7770ad55c | |||
| 1bd3255f14 | |||
| 76d50df466 | |||
| 8c36739ebd | |||
| 560ed80123 | |||
| 2e788ae54d | |||
| a0a5c9b9ca | |||
| 12220ec82d | |||
| 3715fe85aa | |||
| ba6e5f125d | |||
| 1459d3519d | |||
| c9cfe2c727 | |||
| 27a8837422 | |||
| bc979ea6aa | |||
| 79472cdf83 | |||
| adf6f28ebf | |||
| a7dfd36c00 | |||
| 53327edb50 | |||
| 6c7f3751e7 | |||
| c179a3ccb7 | |||
| 13efc421c4 | |||
| 10db1edc7f | |||
| 039b85f355 | |||
| 0b41b42f7b | |||
| a46ff01cab | |||
| e7b6e62b82 | |||
| 3662a8e962 | |||
| 49b6ef7f48 | |||
| 1f9efd9311 | |||
| 7f8dbf24e7 | |||
| 5e4b88201e | |||
| 1252188b4b | |||
| a0c10be1ff | |||
| 15d3a166f7 | |||
| a58db66a98 | |||
| f51c2aeced | |||
| 3edb3243f6 | |||
| c693bc1247 | |||
| 5d3ab260e1 | |||
| ea64c4c0ae | |||
| 84bce8ae02 | |||
| 3c5974930a | |||
| be854df32d | |||
| adbf68f771 | |||
| f62177a827 | |||
| bb40d161e8 | |||
| 84b1f363e4 | |||
| a5348b8679 | |||
| 63c9a85c6a | |||
| adf9ec5360 | |||
| 883d1232d3 | |||
| b58fddb83e | |||
| ce1491df9c | |||
| ea01a53711 | |||
| 85cdb2ed9a | |||
| 368947c88c | |||
| 3f1f7ad89c | |||
| a8cfa2e2fe | |||
| c8eed43538 | |||
| 29a2f576f5 | |||
| 31ec0096e4 | |||
| 8728c7dde3 | |||
| e9a37d99c3 | |||
| 67742699cc | |||
| de975d5af9 | |||
| c3143f3833 | |||
| 1c2f9e6190 | |||
| 654338f9c7 | |||
| dfd7a8c8d7 | |||
| bb8a5bd476 | |||
| e4359e9acb | |||
| 13b8a67ae2 | |||
| 89d9fdca82 | |||
| a478b64ff3 | |||
| b3c2daf9e5 | |||
| 792a9b715c | |||
| 631d6d9ef4 | |||
| 6249211745 | |||
| 2213f8f8a4 | |||
| 4e85568370 | |||
| 88a4f2baf1 |
@@ -12,12 +12,12 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
# Label PRs
|
# Label PRs
|
||||||
- uses: actions/labeler@v5.0.0
|
- uses: actions/labeler@v6
|
||||||
with:
|
with:
|
||||||
dot: true
|
dot: true
|
||||||
configuration-path: .github/labeler.yaml
|
configuration-path: .github/labeler.yaml
|
||||||
@@ -55,13 +55,13 @@ jobs:
|
|||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
issue_number: prNumber
|
issue_number: prNumber
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasDevC3Label = labels.some(label => label.name === process.env.PR_LABEL);
|
const hasDevC3Label = labels.some(label => label.name === process.env.PR_LABEL);
|
||||||
const hasTrustLabel = labels.some(label => label.name === process.env.TRUST_FORK_PR_LABEL);
|
const hasTrustLabel = labels.some(label => label.name === process.env.TRUST_FORK_PR_LABEL);
|
||||||
|
|
||||||
console.log(`PR #${prNumber} has ${process.env.PR_LABEL} label: ${hasDevC3Label}`);
|
console.log(`PR #${prNumber} has ${process.env.PR_LABEL} label: ${hasDevC3Label}`);
|
||||||
console.log(`PR #${prNumber} has ${process.env.TRUST_FORK_PR_LABEL} label: ${hasTrustLabel}`);
|
console.log(`PR #${prNumber} has ${process.env.TRUST_FORK_PR_LABEL} label: ${hasTrustLabel}`);
|
||||||
|
|
||||||
core.setOutput('has-dev', hasDevC3Label ? 'true' : 'false');
|
core.setOutput('has-dev', hasDevC3Label ? 'true' : 'false');
|
||||||
core.setOutput('has-trust', hasTrustLabel ? 'true' : 'false');
|
core.setOutput('has-trust', hasTrustLabel ? 'true' : 'false');
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ jobs:
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Removed '${process.env.TRUST_FORK_PR_LABEL}' label from PR #${prNumber} as it received new commits`);
|
console.log(`Removed '${process.env.TRUST_FORK_PR_LABEL}' label from PR #${prNumber} as it received new commits`);
|
||||||
|
|
||||||
// Add a comment to the PR
|
// Add a comment to the PR
|
||||||
await github.rest.issues.createComment({
|
await github.rest.issues.createComment({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo '${{ needs.setup.outputs.model_matrix }}' > matrix.json
|
echo '${{ needs.setup.outputs.model_matrix }}' > matrix.json
|
||||||
built=(); while IFS= read -r line; do built+=("$line"); done < <(
|
built=(); while IFS= read -r line; do built+=("$line"); done < <(
|
||||||
ls output | sed -E 's/^model-//' | sed -E 's/-[0-9]+$//' | sed -E 's/ \([^)]*\)//' | awk '{gsub(/^ +| +$/, ""); print}'
|
find output -maxdepth 1 -name 'model-*' -printf "%f\n" | sed -E 's/^model-//' | sed -E 's/-[0-9]+$//' | sed -E 's/ \([^)]*\)//' | awk '{gsub(/^ +| +$/, ""); print}'
|
||||||
)
|
)
|
||||||
jq -c --argjson built "$(printf '%s\n' "${built[@]}" | jq -R . | jq -s .)" \
|
jq -c --argjson built "$(printf '%s\n' "${built[@]}" | jq -R . | jq -s .)" \
|
||||||
'map(select(.display_name as $n | ($built | index($n | gsub("^ +| +$"; "")) | not)))' matrix.json > retry_matrix.json
|
'map(select(.display_name as $n | ($built | index($n | gsub("^ +| +$"; "")) | not)))' matrix.json > retry_matrix.json
|
||||||
@@ -168,6 +168,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() && (needs.get_and_build.result != 'failure' || needs.retry_get_and_build.result == 'success' || (needs.retry_failed_models.outputs.retry_matrix != '[]' && needs.retry_failed_models.outputs.retry_matrix != '')) }}
|
if: ${{ !cancelled() && (needs.get_and_build.result != 'failure' || needs.retry_get_and_build.result == 'success' || (needs.retry_failed_models.outputs.retry_matrix != '[]' && needs.retry_failed_models.outputs.retry_matrix != '')) }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
max-parallel: 1
|
max-parallel: 1
|
||||||
matrix:
|
matrix:
|
||||||
model: ${{ fromJson(needs.setup.outputs.model_matrix) }}
|
model: ${{ fromJson(needs.setup.outputs.model_matrix) }}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
if: always() && github.repository == 'commaai/openpilot'
|
if: always() && github.repository == 'commaai/openpilot'
|
||||||
steps:
|
steps:
|
||||||
- name: Get job results
|
- name: Get job results
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
id: get-job-results
|
id: get-job-results
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: commaai/timeout@v1
|
- uses: commaai/timeout@v1
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
||||||
# Push to docs.comma.ai
|
# Push to docs.comma.ai
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
if: github.ref == 'refs/heads/master' && github.repository == 'sunnypilot/sunnypilot'
|
||||||
with:
|
with:
|
||||||
path: openpilot-docs
|
path: openpilot-docs
|
||||||
|
|||||||
@@ -5,7 +5,44 @@ on:
|
|||||||
types: [created, edited]
|
types: [created, edited]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# TODO: gc old branches in a separate job in this workflow
|
cleanup-branches:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Delete stale Jenkins branches
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
||||||
|
const prefixes = ['tmp-jenkins', '__jenkins'];
|
||||||
|
|
||||||
|
for await (const response of github.paginate.iterator(github.rest.repos.listBranches, {
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
per_page: 100,
|
||||||
|
})) {
|
||||||
|
for (const branch of response.data) {
|
||||||
|
if (!prefixes.some(p => branch.name.startsWith(p))) continue;
|
||||||
|
|
||||||
|
const { data: commit } = await github.rest.repos.getCommit({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: branch.commit.sha,
|
||||||
|
});
|
||||||
|
|
||||||
|
const commitDate = new Date(commit.commit.committer.date).getTime();
|
||||||
|
if (commitDate < cutoff) {
|
||||||
|
console.log(`Deleting branch: ${branch.name} (last commit: ${commit.commit.committer.date})`);
|
||||||
|
await github.rest.git.deleteRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: `heads/${branch.name}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scan-comments:
|
scan-comments:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.issue.pull_request }}
|
if: ${{ github.event.issue.pull_request }}
|
||||||
@@ -15,7 +52,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check for trigger phrase
|
- name: Check for trigger phrase
|
||||||
id: check_comment
|
id: check_comment
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const triggerPhrase = "trigger-jenkins";
|
const triggerPhrase = "trigger-jenkins";
|
||||||
@@ -35,7 +72,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
if: steps.check_comment.outputs.result == 'true'
|
if: steps.check_comment.outputs.result == 'true'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: refs/pull/${{ github.event.issue.number }}/head
|
ref: refs/pull/${{ github.event.issue.number }}/head
|
||||||
|
|
||||||
@@ -49,7 +86,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Delete trigger comment
|
- name: Delete trigger comment
|
||||||
if: steps.check_comment.outputs.result == 'true' && always()
|
if: steps.check_comment.outputs.result == 'true' && always()
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
await github.rest.issues.deleteComment({
|
await github.rest.issues.deleteComment({
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
path: ${{ github.workspace }}/pr_ui
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
- name: Getting master ui # filename: master_ui_raylib/mici_ui_replay.mp4
|
- name: Getting master ui # filename: master_ui_raylib/mici_ui_replay.mp4
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: sunnypilot/ci-artifacts
|
repository: sunnypilot/ci-artifacts
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ jobs:
|
|||||||
if: github.repository == 'commaai/openpilot'
|
if: github.repository == 'commaai/openpilot'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Checkout master
|
- name: Checkout master
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
path: base
|
path: base
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
running-workflow-name: 'build prebuilt'
|
running-workflow-name: 'build prebuilt'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build master-ci).*).)*$
|
check-regexp: ^((?!.*(build master-ci).*).)*$
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: git lfs pull
|
- run: git lfs pull
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
path: ${{ github.workspace }}/pr_ui
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
- name: Getting master ui
|
- name: Getting master ui
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: sunnypilot/ci-artifacts
|
repository: sunnypilot/ci-artifacts
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
running-workflow-name: 'build __nightly'
|
running-workflow-name: 'build __nightly'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ on:
|
|||||||
env:
|
env:
|
||||||
BASE_IMAGE: sunnypilot-base
|
BASE_IMAGE: sunnypilot-base
|
||||||
BUILD: release/ci/docker_build_sp.sh base
|
BUILD: release/ci/docker_build_sp.sh base
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update_translations:
|
update_translations:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Update translations
|
- name: Update translations
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||||
with:
|
with:
|
||||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||||
commit-message: "Update translations"
|
commit-message: "Update translations"
|
||||||
@@ -39,18 +39,26 @@ jobs:
|
|||||||
image: ghcr.io/sunnypilot/sunnypilot-base:latest
|
image: ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: uv lock
|
- name: uv lock
|
||||||
if: github.repository == 'commaai/openpilot'
|
|
||||||
run: |
|
run: |
|
||||||
python3 -m ensurepip --upgrade
|
python3 -m ensurepip --upgrade
|
||||||
pip3 install uv
|
pip3 install uv
|
||||||
uv lock --upgrade
|
uv lock --upgrade
|
||||||
|
- name: uv pip tree
|
||||||
|
id: pip_tree
|
||||||
|
run: |
|
||||||
|
echo 'PIP_TREE<<EOF' >> $GITHUB_OUTPUT
|
||||||
|
uv pip tree >> $GITHUB_OUTPUT
|
||||||
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
- name: bump submodules
|
- name: bump submodules
|
||||||
run: |
|
run: |
|
||||||
git config --global --add safe.directory '*'
|
git config --global --add safe.directory '*'
|
||||||
|
git config submodule.msgq.update none
|
||||||
|
git config submodule.rednose_repo.update none
|
||||||
|
git config submodule.teleoprtc_repo.update none
|
||||||
git config submodule.tinygrad.update none
|
git config submodule.tinygrad.update none
|
||||||
git submodule update --remote
|
git submodule update --remote
|
||||||
git add .
|
git add .
|
||||||
@@ -61,7 +69,7 @@ jobs:
|
|||||||
python selfdrive/car/docs.py
|
python selfdrive/car/docs.py
|
||||||
git add docs/CARS.md
|
git add docs/CARS.md
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||||
with:
|
with:
|
||||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||||
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
|
token: ${{ github.repository == 'commaai/openpilot' && secrets.ACTIONS_CREATE_PR_PAT || secrets.GITHUB_TOKEN }}
|
||||||
@@ -70,5 +78,10 @@ jobs:
|
|||||||
branch: auto-package-updates
|
branch: auto-package-updates
|
||||||
base: master
|
base: master
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
body: 'Automatic PR from repo-maintenance -> package_updates'
|
body: |
|
||||||
|
Automatic PR from repo-maintenance -> package_updates
|
||||||
|
|
||||||
|
```
|
||||||
|
${{ steps.pip_tree.outputs.PIP_TREE }}
|
||||||
|
```
|
||||||
labels: bot
|
labels: bot
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
exempt-all-milestones: true
|
exempt-all-milestones: true
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
stale_drafts:
|
stale_drafts:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
exempt-all-milestones: true
|
exempt-all-milestones: true
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,10 @@ concurrency:
|
|||||||
env:
|
env:
|
||||||
PYTHONWARNINGS: error
|
PYTHONWARNINGS: error
|
||||||
BASE_IMAGE: sunnypilot-base
|
BASE_IMAGE: sunnypilot-base
|
||||||
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
|
||||||
|
|
||||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||||
BUILD: release/ci/docker_build_sp.sh base
|
BUILD: release/ci/docker_build_sp.sh base
|
||||||
|
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||||
|
|
||||||
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
||||||
|
|
||||||
@@ -41,7 +39,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
STRIPPED_DIR: /tmp/releasepilot
|
STRIPPED_DIR: /tmp/releasepilot
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Getting LFS files
|
- name: Getting LFS files
|
||||||
@@ -93,7 +91,7 @@ jobs:
|
|||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Setup docker push
|
- name: Setup docker push
|
||||||
@@ -107,10 +105,10 @@ jobs:
|
|||||||
|
|
||||||
build_mac:
|
build_mac:
|
||||||
name: build macOS
|
name: build macOS
|
||||||
|
if: false # tmp disable due to brew install not working
|
||||||
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||||
if: false # There'll be one day that this works. That day is not today.
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||||
@@ -164,7 +162,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
PYTHONWARNINGS: default
|
PYTHONWARNINGS: default
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
@@ -181,7 +179,7 @@ jobs:
|
|||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
@@ -207,22 +205,23 @@ jobs:
|
|||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
id: setup-step
|
id: setup-step
|
||||||
- name: Cache test routes
|
- name: Cache test routes
|
||||||
id: dependency-cache
|
id: dependency-cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: .ci_cache/comma_download_cache
|
path: .ci_cache/comma_download_cache
|
||||||
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/ref_commit', 'selfdrive/test/process_replay/test_processes.py') }}
|
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/test_processes.py') }}
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "scons -j$(nproc)"
|
${{ env.RUN }} "scons -j$(nproc)"
|
||||||
- name: Run replay
|
- name: Run replay
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
|
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
|
||||||
|
continue-on-error: ${{ github.ref == 'refs/heads/master' }}
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
chmod -R 777 /tmp/comma_download_cache"
|
||||||
@@ -230,16 +229,32 @@ jobs:
|
|||||||
id: print-diff
|
id: print-diff
|
||||||
if: always()
|
if: always()
|
||||||
run: cat selfdrive/test/process_replay/diff.txt
|
run: cat selfdrive/test/process_replay/diff.txt
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v6
|
||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: process_replay_diff.txt
|
name: process_replay_diff.txt
|
||||||
path: selfdrive/test/process_replay/diff.txt
|
path: selfdrive/test/process_replay/diff.txt
|
||||||
- name: Upload reference logs
|
- name: Checkout ci-artifacts
|
||||||
if: false # TODO: move this to github instead of azure
|
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: commaai/ci-artifacts
|
||||||
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
path: ${{ github.workspace }}/ci-artifacts
|
||||||
|
- name: Push refs
|
||||||
|
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||||
|
working-directory: ${{ github.workspace }}/ci-artifacts
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "unset PYTHONWARNINGS && AZURE_TOKEN='$AZURE_TOKEN' python3 selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
git checkout --orphan process-replay
|
||||||
|
git rm -rf .
|
||||||
|
git config user.name "GitHub Actions Bot"
|
||||||
|
git config user.email "<>"
|
||||||
|
cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst .
|
||||||
|
echo "${{ github.sha }}" > ref_commit
|
||||||
|
git add .
|
||||||
|
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}"
|
||||||
|
git push origin process-replay --force
|
||||||
- name: Run regen
|
- name: Run regen
|
||||||
if: false
|
if: false
|
||||||
timeout-minutes: 4
|
timeout-minutes: 4
|
||||||
@@ -257,7 +272,7 @@ jobs:
|
|||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
if: false # FIXME: Started to timeout recently
|
if: false # FIXME: Started to timeout recently
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
@@ -281,7 +296,7 @@ jobs:
|
|||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
@@ -293,7 +308,7 @@ jobs:
|
|||||||
source selfdrive/test/setup_xvfb.sh &&
|
source selfdrive/test/setup_xvfb.sh &&
|
||||||
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
|
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
|
||||||
- name: Upload Raylib UI Report
|
- name: Upload Raylib UI Report
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
|
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
|
||||||
@@ -307,7 +322,7 @@ jobs:
|
|||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
@@ -319,7 +334,7 @@ jobs:
|
|||||||
source selfdrive/test/setup_xvfb.sh &&
|
source selfdrive/test/setup_xvfb.sh &&
|
||||||
WINDOWED=1 python3 selfdrive/ui/tests/diff/replay.py"
|
WINDOWED=1 python3 selfdrive/ui/tests/diff/replay.py"
|
||||||
- name: Upload Raylib UI Report
|
- name: Upload Raylib UI Report
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: mici-raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
name: mici-raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
path: selfdrive/ui/tests/diff/report
|
path: selfdrive/ui/tests/diff/report
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
name: vendor third_party
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: github.ref != 'refs/heads/master'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-24.04, macos-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Build
|
||||||
|
run: third_party/build.sh
|
||||||
|
- name: Package artifacts
|
||||||
|
run: |
|
||||||
|
git add -A third_party/
|
||||||
|
git diff --cached --name-only -- third_party/ | tar -cf /tmp/third_party_build.tar -T -
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: third-party-${{ runner.os }}
|
||||||
|
path: /tmp/third_party_build.tar
|
||||||
|
|
||||||
|
commit:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/artifacts
|
||||||
|
- name: Commit vendored libraries
|
||||||
|
run: |
|
||||||
|
for f in /tmp/artifacts/*/third_party_build.tar; do
|
||||||
|
tar xf "$f"
|
||||||
|
done
|
||||||
|
git add third_party/
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git commit -m "third_party: rebuild vendor libraries"
|
||||||
|
git push
|
||||||
@@ -99,6 +99,11 @@ Pipfile
|
|||||||
.history
|
.history
|
||||||
.ionide
|
.ionide
|
||||||
|
|
||||||
|
.claude/
|
||||||
|
.context/
|
||||||
|
PLAN.md
|
||||||
|
TASK.md
|
||||||
|
|
||||||
### JetBrains ###
|
### JetBrains ###
|
||||||
!.idea/customTargets.xml
|
!.idea/customTargets.xml
|
||||||
!.idea/tools/*
|
!.idea/tools/*
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
url = https://github.com/commaai/teleoprtc
|
url = https://github.com/commaai/teleoprtc
|
||||||
[submodule "tinygrad"]
|
[submodule "tinygrad"]
|
||||||
path = tinygrad_repo
|
path = tinygrad_repo
|
||||||
url = https://github.com/commaai/tinygrad.git
|
url = https://github.com/sunnypilot/tinygrad.git
|
||||||
[submodule "sunnypilot/neural_network_data"]
|
[submodule "sunnypilot/neural_network_data"]
|
||||||
path = sunnypilot/neural_network_data
|
path = sunnypilot/neural_network_data
|
||||||
url = https://github.com/sunnypilot/neural-network-data.git
|
url = https://github.com/sunnypilot/neural-network-data.git
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ WORKDIR ${OPENPILOT_PATH}
|
|||||||
|
|
||||||
COPY . ${OPENPILOT_PATH}/
|
COPY . ${OPENPILOT_PATH}/
|
||||||
|
|
||||||
RUN scons --cache-readonly -j$(nproc)
|
ENV UV_BIN="/home/batman/.local/bin/"
|
||||||
|
ENV PATH="$UV_BIN:$PATH"
|
||||||
|
RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ shopt -s huponexit # kill all child processes when the shell exits
|
|||||||
|
|
||||||
export CI=1
|
export CI=1
|
||||||
export PYTHONWARNINGS=error
|
export PYTHONWARNINGS=error
|
||||||
export LOGPRINT=debug
|
#export LOGPRINT=debug # this has gotten too spammy...
|
||||||
export TEST_DIR=${env.TEST_DIR}
|
export TEST_DIR=${env.TEST_DIR}
|
||||||
export SOURCE_DIR=${env.SOURCE_DIR}
|
export SOURCE_DIR=${env.SOURCE_DIR}
|
||||||
export GIT_BRANCH=${env.GIT_BRANCH}
|
export GIT_BRANCH=${env.GIT_BRANCH}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
Version 0.10.4 (2026-02-17)
|
||||||
|
========================
|
||||||
|
* Lexus LS 2018 support thanks to Hacheoy!
|
||||||
|
|
||||||
Version 0.10.3 (2025-12-17)
|
Version 0.10.3 (2025-12-17)
|
||||||
========================
|
========================
|
||||||
* New driving model #36249
|
* New driving model #36249
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ Decider('MD5-timestamp')
|
|||||||
|
|
||||||
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
|
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
|
||||||
|
|
||||||
AddOption('--kaitai', action='store_true', help='Regenerate kaitai struct parsers')
|
|
||||||
AddOption('--asan', action='store_true', help='turn on ASAN')
|
AddOption('--asan', action='store_true', help='turn on ASAN')
|
||||||
AddOption('--ubsan', action='store_true', help='turn on UBSan')
|
AddOption('--ubsan', action='store_true', help='turn on UBSan')
|
||||||
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
|
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
|
||||||
@@ -203,7 +202,6 @@ SConscript(['rednose/SConscript'])
|
|||||||
|
|
||||||
# Build system services
|
# Build system services
|
||||||
SConscript([
|
SConscript([
|
||||||
'system/ubloxd/SConscript',
|
|
||||||
'system/loggerd/SConscript',
|
'system/loggerd/SConscript',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files])
|
|||||||
|
|
||||||
# Build messaging
|
# Build messaging
|
||||||
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
||||||
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, common, 'pthread'])
|
env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc', 'messaging/bridge_zmq.cc'], LIBS=[msgq, common, 'pthread'])
|
||||||
|
|
||||||
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
|
socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc'])
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
|
|||||||
navigation @1;
|
navigation @1;
|
||||||
vision @2;
|
vision @2;
|
||||||
policy @3;
|
policy @3;
|
||||||
|
offPolicy @4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
|||||||
laneChange @50;
|
laneChange @50;
|
||||||
lowMemory @51;
|
lowMemory @51;
|
||||||
stockAeb @52;
|
stockAeb @52;
|
||||||
|
stockLkas @98;
|
||||||
ldw @53;
|
ldw @53;
|
||||||
carUnrecognized @54;
|
carUnrecognized @54;
|
||||||
invalidLkasSetting @55;
|
invalidLkasSetting @55;
|
||||||
@@ -1477,6 +1478,11 @@ struct ProcLog {
|
|||||||
|
|
||||||
cmdline @15 :List(Text);
|
cmdline @15 :List(Text);
|
||||||
exe @16 :Text;
|
exe @16 :Text;
|
||||||
|
|
||||||
|
# from /proc/<pid>/smaps_rollup (proportional/private memory)
|
||||||
|
memPss @17 :UInt64; # Pss — shared pages split by mapper count
|
||||||
|
memPssAnon @18 :UInt64; # Pss_Anon — private anonymous (heap, stack)
|
||||||
|
memPssShmem @19 :UInt64; # Pss_Shmem — proportional MSGQ/tmpfs share
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CPUTimes {
|
struct CPUTimes {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
# must be built with scons
|
# must be built with scons
|
||||||
from msgq.ipc_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
from msgq import fake_event_handle, drain_sock_raw, MultiplePublishersError, IpcError, \
|
||||||
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
||||||
from msgq.ipc_pyx import MultiplePublishersError, IpcError
|
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
||||||
from msgq import fake_event_handle, drain_sock_raw
|
|
||||||
import msgq
|
import msgq
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import capnp
|
import capnp
|
||||||
import time
|
import time
|
||||||
@@ -13,7 +11,7 @@ from typing import Optional, List, Union, Dict
|
|||||||
|
|
||||||
from cereal import log
|
from cereal import log
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
from openpilot.common.util import MovingAverage
|
from openpilot.common.utils import MovingAverage
|
||||||
|
|
||||||
NO_TRAVERSAL_LIMIT = 2**64-1
|
NO_TRAVERSAL_LIMIT = 2**64-1
|
||||||
|
|
||||||
|
|||||||
@@ -25,15 +25,16 @@ void msgq_to_zmq(const std::vector<std::string> &endpoints, const std::string &i
|
|||||||
}
|
}
|
||||||
|
|
||||||
void zmq_to_msgq(const std::vector<std::string> &endpoints, const std::string &ip) {
|
void zmq_to_msgq(const std::vector<std::string> &endpoints, const std::string &ip) {
|
||||||
auto poller = std::make_unique<ZMQPoller>();
|
auto poller = std::make_unique<BridgeZmqPoller>();
|
||||||
auto pub_context = std::make_unique<MSGQContext>();
|
auto pub_context = std::make_unique<Context>();
|
||||||
auto sub_context = std::make_unique<ZMQContext>();
|
auto sub_context = std::make_unique<BridgeZmqContext>();
|
||||||
std::map<SubSocket *, PubSocket *> sub2pub;
|
std::map<BridgeZmqSubSocket *, PubSocket *> sub2pub;
|
||||||
|
|
||||||
for (auto endpoint : endpoints) {
|
for (auto endpoint : endpoints) {
|
||||||
auto pub_sock = new MSGQPubSocket();
|
auto pub_sock = new PubSocket();
|
||||||
auto sub_sock = new ZMQSubSocket();
|
auto sub_sock = new BridgeZmqSubSocket();
|
||||||
pub_sock->connect(pub_context.get(), endpoint);
|
size_t queue_size = services.at(endpoint).queue_size;
|
||||||
|
pub_sock->connect(pub_context.get(), endpoint, true, queue_size);
|
||||||
sub_sock->connect(sub_context.get(), endpoint, ip, false);
|
sub_sock->connect(sub_context.get(), endpoint, ip, false);
|
||||||
|
|
||||||
poller->registerSocket(sub_sock);
|
poller->registerSocket(sub_sock);
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
#include "cereal/messaging/bridge_zmq.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static size_t fnv1a_hash(const std::string &str) {
|
||||||
|
const size_t fnv_prime = 0x100000001b3;
|
||||||
|
size_t hash_value = 0xcbf29ce484222325;
|
||||||
|
for (char c : str) {
|
||||||
|
hash_value ^= (unsigned char)c;
|
||||||
|
hash_value *= fnv_prime;
|
||||||
|
}
|
||||||
|
return hash_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This is a hack to get the port number from the socket name, might have collisions.
|
||||||
|
static int get_port(std::string endpoint) {
|
||||||
|
size_t hash_value = fnv1a_hash(endpoint);
|
||||||
|
int start_port = 8023;
|
||||||
|
int max_port = 65535;
|
||||||
|
return start_port + (hash_value % (max_port - start_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqContext::BridgeZmqContext() {
|
||||||
|
context = zmq_ctx_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqContext::~BridgeZmqContext() {
|
||||||
|
if (context != nullptr) {
|
||||||
|
zmq_ctx_term(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqMessage::init(size_t sz) {
|
||||||
|
size = sz;
|
||||||
|
data = new char[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqMessage::init(char *d, size_t sz) {
|
||||||
|
size = sz;
|
||||||
|
data = new char[size];
|
||||||
|
memcpy(data, d, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqMessage::close() {
|
||||||
|
if (size > 0) {
|
||||||
|
delete[] data;
|
||||||
|
}
|
||||||
|
data = nullptr;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqMessage::~BridgeZmqMessage() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqSubSocket::connect(BridgeZmqContext *context, std::string endpoint, std::string address, bool conflate, bool check_endpoint) {
|
||||||
|
sock = zmq_socket(context->getRawContext(), ZMQ_SUB);
|
||||||
|
if (sock == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "", 0);
|
||||||
|
|
||||||
|
if (conflate) {
|
||||||
|
int arg = 1;
|
||||||
|
zmq_setsockopt(sock, ZMQ_CONFLATE, &arg, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
int reconnect_ivl = 500;
|
||||||
|
zmq_setsockopt(sock, ZMQ_RECONNECT_IVL_MAX, &reconnect_ivl, sizeof(reconnect_ivl));
|
||||||
|
|
||||||
|
full_endpoint = "tcp://" + address + ":";
|
||||||
|
if (check_endpoint) {
|
||||||
|
full_endpoint += std::to_string(get_port(endpoint));
|
||||||
|
} else {
|
||||||
|
full_endpoint += endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return zmq_connect(sock, full_endpoint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqSubSocket::setTimeout(int timeout) {
|
||||||
|
zmq_setsockopt(sock, ZMQ_RCVTIMEO, &timeout, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
Message *BridgeZmqSubSocket::receive(bool non_blocking) {
|
||||||
|
zmq_msg_t msg;
|
||||||
|
assert(zmq_msg_init(&msg) == 0);
|
||||||
|
|
||||||
|
int flags = non_blocking ? ZMQ_DONTWAIT : 0;
|
||||||
|
int rc = zmq_msg_recv(&msg, sock, flags);
|
||||||
|
|
||||||
|
Message *ret = nullptr;
|
||||||
|
if (rc >= 0) {
|
||||||
|
ret = new BridgeZmqMessage;
|
||||||
|
ret->init((char *)zmq_msg_data(&msg), zmq_msg_size(&msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq_msg_close(&msg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqSubSocket::~BridgeZmqSubSocket() {
|
||||||
|
if (sock != nullptr) {
|
||||||
|
zmq_close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqPubSocket::connect(BridgeZmqContext *context, std::string endpoint, bool check_endpoint) {
|
||||||
|
sock = zmq_socket(context->getRawContext(), ZMQ_PUB);
|
||||||
|
if (sock == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_endpoint = "tcp://*:";
|
||||||
|
if (check_endpoint) {
|
||||||
|
full_endpoint += std::to_string(get_port(endpoint));
|
||||||
|
} else {
|
||||||
|
full_endpoint += endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZMQ pub sockets cannot be shared between processes, so we need to ensure pid stays the same.
|
||||||
|
pid = getpid();
|
||||||
|
|
||||||
|
return zmq_bind(sock, full_endpoint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqPubSocket::sendMessage(Message *message) {
|
||||||
|
assert(pid == getpid());
|
||||||
|
return zmq_send(sock, message->getData(), message->getSize(), ZMQ_DONTWAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BridgeZmqPubSocket::send(char *data, size_t size) {
|
||||||
|
assert(pid == getpid());
|
||||||
|
return zmq_send(sock, data, size, ZMQ_DONTWAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
BridgeZmqPubSocket::~BridgeZmqPubSocket() {
|
||||||
|
if (sock != nullptr) {
|
||||||
|
zmq_close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BridgeZmqPoller::registerSocket(BridgeZmqSubSocket *socket) {
|
||||||
|
assert(num_polls + 1 < (sizeof(polls) / sizeof(polls[0])));
|
||||||
|
polls[num_polls].socket = socket->getRawSocket();
|
||||||
|
polls[num_polls].events = ZMQ_POLLIN;
|
||||||
|
|
||||||
|
sockets.push_back(socket);
|
||||||
|
num_polls++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BridgeZmqSubSocket *> BridgeZmqPoller::poll(int timeout) {
|
||||||
|
std::vector<BridgeZmqSubSocket *> ret;
|
||||||
|
|
||||||
|
int rc = zmq_poll(polls, num_polls, timeout);
|
||||||
|
if (rc < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_polls; i++) {
|
||||||
|
if (polls[i].revents) {
|
||||||
|
ret.push_back(sockets[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <zmq.h>
|
||||||
|
|
||||||
|
#include "msgq/ipc.h"
|
||||||
|
|
||||||
|
class BridgeZmqContext {
|
||||||
|
public:
|
||||||
|
BridgeZmqContext();
|
||||||
|
void *getRawContext() { return context; }
|
||||||
|
~BridgeZmqContext();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *context = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqMessage : public Message {
|
||||||
|
public:
|
||||||
|
void init(size_t size);
|
||||||
|
void init(char *data, size_t size);
|
||||||
|
void close();
|
||||||
|
size_t getSize() { return size; }
|
||||||
|
char *getData() { return data; }
|
||||||
|
~BridgeZmqMessage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *data = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqSubSocket {
|
||||||
|
public:
|
||||||
|
int connect(BridgeZmqContext *context, std::string endpoint, std::string address, bool conflate = false, bool check_endpoint = true);
|
||||||
|
void setTimeout(int timeout);
|
||||||
|
Message *receive(bool non_blocking = false);
|
||||||
|
void *getRawSocket() { return sock; }
|
||||||
|
~BridgeZmqSubSocket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *sock = nullptr;
|
||||||
|
std::string full_endpoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqPubSocket {
|
||||||
|
public:
|
||||||
|
int connect(BridgeZmqContext *context, std::string endpoint, bool check_endpoint = true);
|
||||||
|
int sendMessage(Message *message);
|
||||||
|
int send(char *data, size_t size);
|
||||||
|
void *getRawSocket() { return sock; }
|
||||||
|
~BridgeZmqPubSocket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *sock = nullptr;
|
||||||
|
std::string full_endpoint;
|
||||||
|
int pid = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BridgeZmqPoller {
|
||||||
|
public:
|
||||||
|
void registerSocket(BridgeZmqSubSocket *socket);
|
||||||
|
std::vector<BridgeZmqSubSocket *> poll(int timeout);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t MAX_BRIDGE_ZMQ_POLLERS = 128;
|
||||||
|
std::vector<BridgeZmqSubSocket *> sockets;
|
||||||
|
zmq_pollitem_t polls[MAX_BRIDGE_ZMQ_POLLERS] = {};
|
||||||
|
size_t num_polls = 0;
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "cereal/services.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
extern ExitHandler do_exit;
|
extern ExitHandler do_exit;
|
||||||
@@ -21,14 +22,14 @@ static std::string recv_zmq_msg(void *sock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string &ip) {
|
void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string &ip) {
|
||||||
zmq_context = std::make_unique<ZMQContext>();
|
zmq_context = std::make_unique<BridgeZmqContext>();
|
||||||
msgq_context = std::make_unique<MSGQContext>();
|
msgq_context = std::make_unique<Context>();
|
||||||
|
|
||||||
// Create ZMQPubSockets for each endpoint
|
// Create ZMQPubSockets for each endpoint
|
||||||
for (const auto &endpoint : endpoints) {
|
for (const auto &endpoint : endpoints) {
|
||||||
auto &socket_pair = socket_pairs.emplace_back();
|
auto &socket_pair = socket_pairs.emplace_back();
|
||||||
socket_pair.endpoint = endpoint;
|
socket_pair.endpoint = endpoint;
|
||||||
socket_pair.pub_sock = std::make_unique<ZMQPubSocket>();
|
socket_pair.pub_sock = std::make_unique<BridgeZmqPubSocket>();
|
||||||
int ret = socket_pair.pub_sock->connect(zmq_context.get(), endpoint);
|
int ret = socket_pair.pub_sock->connect(zmq_context.get(), endpoint);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
printf("Failed to create ZMQ publisher for [%s]: %s\n", endpoint.c_str(), zmq_strerror(zmq_errno()));
|
printf("Failed to create ZMQ publisher for [%s]: %s\n", endpoint.c_str(), zmq_strerror(zmq_errno()));
|
||||||
@@ -48,7 +49,7 @@ void MsgqToZmq::run(const std::vector<std::string> &endpoints, const std::string
|
|||||||
|
|
||||||
for (auto sub_sock : msgq_poller->poll(100)) {
|
for (auto sub_sock : msgq_poller->poll(100)) {
|
||||||
// Process messages for each socket
|
// Process messages for each socket
|
||||||
ZMQPubSocket *pub_sock = sub2pub.at(sub_sock);
|
BridgeZmqPubSocket *pub_sock = sub2pub.at(sub_sock);
|
||||||
for (int i = 0; i < MAX_MESSAGES_PER_SOCKET; ++i) {
|
for (int i = 0; i < MAX_MESSAGES_PER_SOCKET; ++i) {
|
||||||
auto msg = std::unique_ptr<Message>(sub_sock->receive(true));
|
auto msg = std::unique_ptr<Message>(sub_sock->receive(true));
|
||||||
if (!msg) break;
|
if (!msg) break;
|
||||||
@@ -71,7 +72,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
// Set up ZMQ monitor for each pub socket
|
// Set up ZMQ monitor for each pub socket
|
||||||
for (int i = 0; i < socket_pairs.size(); ++i) {
|
for (int i = 0; i < socket_pairs.size(); ++i) {
|
||||||
std::string addr = "inproc://op-bridge-monitor-" + std::to_string(i);
|
std::string addr = "inproc://op-bridge-monitor-" + std::to_string(i);
|
||||||
zmq_socket_monitor(socket_pairs[i].pub_sock->sock, addr.c_str(), ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED);
|
zmq_socket_monitor(socket_pairs[i].pub_sock->getRawSocket(), addr.c_str(), ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED);
|
||||||
|
|
||||||
void *monitor_socket = zmq_socket(zmq_context->getRawContext(), ZMQ_PAIR);
|
void *monitor_socket = zmq_socket(zmq_context->getRawContext(), ZMQ_PAIR);
|
||||||
zmq_connect(monitor_socket, addr.c_str());
|
zmq_connect(monitor_socket, addr.c_str());
|
||||||
@@ -108,7 +109,8 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
if (++pair.connected_clients == 1) {
|
if (++pair.connected_clients == 1) {
|
||||||
// Create new MSGQ subscriber socket and map to ZMQ publisher
|
// Create new MSGQ subscriber socket and map to ZMQ publisher
|
||||||
pair.sub_sock = std::make_unique<MSGQSubSocket>();
|
pair.sub_sock = std::make_unique<MSGQSubSocket>();
|
||||||
pair.sub_sock->connect(msgq_context.get(), pair.endpoint, "127.0.0.1");
|
size_t queue_size = services.at(pair.endpoint).queue_size;
|
||||||
|
pair.sub_sock->connect(msgq_context.get(), pair.endpoint, "127.0.0.1", false, true, queue_size);
|
||||||
sub2pub[pair.sub_sock.get()] = pair.pub_sock.get();
|
sub2pub[pair.sub_sock.get()] = pair.pub_sock.get();
|
||||||
registerSockets();
|
registerSockets();
|
||||||
}
|
}
|
||||||
@@ -128,7 +130,7 @@ void MsgqToZmq::zmqMonitorThread() {
|
|||||||
|
|
||||||
// Clean up monitor sockets
|
// Clean up monitor sockets
|
||||||
for (int i = 0; i < pollitems.size(); ++i) {
|
for (int i = 0; i < pollitems.size(); ++i) {
|
||||||
zmq_socket_monitor(socket_pairs[i].pub_sock->sock, nullptr, 0);
|
zmq_socket_monitor(socket_pairs[i].pub_sock->getRawSocket(), nullptr, 0);
|
||||||
zmq_close(pollitems[i].socket);
|
zmq_close(pollitems[i].socket);
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
|||||||
@@ -7,9 +7,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define private public
|
|
||||||
#include "msgq/impl_msgq.h"
|
#include "msgq/impl_msgq.h"
|
||||||
#include "msgq/impl_zmq.h"
|
#include "cereal/messaging/bridge_zmq.h"
|
||||||
|
|
||||||
class MsgqToZmq {
|
class MsgqToZmq {
|
||||||
public:
|
public:
|
||||||
@@ -22,16 +21,16 @@ protected:
|
|||||||
|
|
||||||
struct SocketPair {
|
struct SocketPair {
|
||||||
std::string endpoint;
|
std::string endpoint;
|
||||||
std::unique_ptr<ZMQPubSocket> pub_sock;
|
std::unique_ptr<BridgeZmqPubSocket> pub_sock;
|
||||||
std::unique_ptr<MSGQSubSocket> sub_sock;
|
std::unique_ptr<MSGQSubSocket> sub_sock;
|
||||||
int connected_clients = 0;
|
int connected_clients = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<MSGQContext> msgq_context;
|
std::unique_ptr<Context> msgq_context;
|
||||||
std::unique_ptr<ZMQContext> zmq_context;
|
std::unique_ptr<BridgeZmqContext> zmq_context;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::unique_ptr<MSGQPoller> msgq_poller;
|
std::unique_ptr<MSGQPoller> msgq_poller;
|
||||||
std::map<SubSocket *, ZMQPubSocket *> sub2pub;
|
std::map<SubSocket *, BridgeZmqPubSocket *> sub2pub;
|
||||||
std::vector<SocketPair> socket_pairs;
|
std::vector<SocketPair> socket_pairs;
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-6
@@ -19,11 +19,6 @@ if GetOption('extras'):
|
|||||||
# Cython bindings
|
# Cython bindings
|
||||||
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
||||||
|
|
||||||
SConscript([
|
common_python = [params_python]
|
||||||
'transformations/SConscript',
|
|
||||||
])
|
|
||||||
|
|
||||||
Import('transformations_python')
|
|
||||||
common_python = [params_python, transformations_python]
|
|
||||||
|
|
||||||
Export('common_python')
|
Export('common_python')
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ class Api:
|
|||||||
return self.service.get_token(payload_extra, expiry_hours)
|
return self.service.get_token(payload_extra, expiry_hours)
|
||||||
|
|
||||||
|
|
||||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
|
||||||
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
|
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
|
||||||
|
|
||||||
|
|
||||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||||
|
|||||||
+4
-2
@@ -51,7 +51,7 @@ class BaseApi:
|
|||||||
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
|
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
|
||||||
return ascii_encoded_text.decode()
|
return ascii_encoded_text.decode()
|
||||||
|
|
||||||
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, json=None, **params):
|
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, session=None, json=None, **params):
|
||||||
headers = {}
|
headers = {}
|
||||||
if access_token is not None:
|
if access_token is not None:
|
||||||
headers['Authorization'] = "JWT " + access_token
|
headers['Authorization'] = "JWT " + access_token
|
||||||
@@ -59,7 +59,9 @@ class BaseApi:
|
|||||||
version = self.remove_non_ascii_chars(get_version())
|
version = self.remove_non_ascii_chars(get_version())
|
||||||
headers['User-Agent'] = self.user_agent + version
|
headers['User-Agent'] = self.user_agent + version
|
||||||
|
|
||||||
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
# TODO: add session to Api
|
||||||
|
req = requests if session is None else session
|
||||||
|
return req.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||||
|
|||||||
+6
-6
@@ -4,27 +4,27 @@ from openpilot.common.utils import run_cmd, run_cmd_default
|
|||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_commit(cwd: str = None, branch: str = "HEAD") -> str:
|
def get_commit(cwd: str | None = None, branch: str = "HEAD") -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", branch], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", branch], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_commit_date(cwd: str = None, commit: str = "HEAD") -> str:
|
def get_commit_date(cwd: str | None = None, commit: str = "HEAD") -> str:
|
||||||
return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd)
|
return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_short_branch(cwd: str = None) -> str:
|
def get_short_branch(cwd: str | None = None) -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_branch(cwd: str = None) -> str:
|
def get_branch(cwd: str | None = None) -> str:
|
||||||
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd)
|
return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_origin(cwd: str = None) -> str:
|
def get_origin(cwd: str | None = None) -> str:
|
||||||
try:
|
try:
|
||||||
local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd)
|
local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd)
|
||||||
tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd)
|
tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd)
|
||||||
@@ -34,7 +34,7 @@ def get_origin(cwd: str = None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_normalized_origin(cwd: str = None) -> str:
|
def get_normalized_origin(cwd: str | None = None) -> str:
|
||||||
return get_origin(cwd) \
|
return get_origin(cwd) \
|
||||||
.replace("git@", "", 1) \
|
.replace("git@", "", 1) \
|
||||||
.replace(".git", "", 1) \
|
.replace(".git", "", 1) \
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import os
|
||||||
|
import fcntl
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
# I2C constants from /usr/include/linux/i2c-dev.h
|
||||||
|
I2C_SLAVE = 0x0703
|
||||||
|
I2C_SLAVE_FORCE = 0x0706
|
||||||
|
I2C_SMBUS = 0x0720
|
||||||
|
|
||||||
|
# SMBus transfer types
|
||||||
|
I2C_SMBUS_READ = 1
|
||||||
|
I2C_SMBUS_WRITE = 0
|
||||||
|
I2C_SMBUS_BYTE_DATA = 2
|
||||||
|
I2C_SMBUS_I2C_BLOCK_DATA = 8
|
||||||
|
|
||||||
|
I2C_SMBUS_BLOCK_MAX = 32
|
||||||
|
|
||||||
|
|
||||||
|
class _I2cSmbusData(ctypes.Union):
|
||||||
|
_fields_ = [
|
||||||
|
("byte", ctypes.c_uint8),
|
||||||
|
("word", ctypes.c_uint16),
|
||||||
|
("block", ctypes.c_uint8 * (I2C_SMBUS_BLOCK_MAX + 2)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _I2cSmbusIoctlData(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("read_write", ctypes.c_uint8),
|
||||||
|
("command", ctypes.c_uint8),
|
||||||
|
("size", ctypes.c_uint32),
|
||||||
|
("data", ctypes.POINTER(_I2cSmbusData)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SMBus:
|
||||||
|
def __init__(self, bus: int):
|
||||||
|
self._fd = os.open(f'/dev/i2c-{bus}', os.O_RDWR)
|
||||||
|
|
||||||
|
def __enter__(self) -> 'SMBus':
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args) -> None:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if hasattr(self, '_fd') and self._fd >= 0:
|
||||||
|
os.close(self._fd)
|
||||||
|
self._fd = -1
|
||||||
|
|
||||||
|
def _set_address(self, addr: int, force: bool = False) -> None:
|
||||||
|
ioctl_arg = I2C_SLAVE_FORCE if force else I2C_SLAVE
|
||||||
|
fcntl.ioctl(self._fd, ioctl_arg, addr)
|
||||||
|
|
||||||
|
def _smbus_access(self, read_write: int, command: int, size: int, data: _I2cSmbusData) -> None:
|
||||||
|
ioctl_data = _I2cSmbusIoctlData(read_write, command, size, ctypes.pointer(data))
|
||||||
|
fcntl.ioctl(self._fd, I2C_SMBUS, ioctl_data)
|
||||||
|
|
||||||
|
def read_byte_data(self, addr: int, register: int, force: bool = False) -> int:
|
||||||
|
self._set_address(addr, force)
|
||||||
|
data = _I2cSmbusData()
|
||||||
|
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_BYTE_DATA, data)
|
||||||
|
return int(data.byte)
|
||||||
|
|
||||||
|
def write_byte_data(self, addr: int, register: int, value: int, force: bool = False) -> None:
|
||||||
|
self._set_address(addr, force)
|
||||||
|
data = _I2cSmbusData()
|
||||||
|
data.byte = value & 0xFF
|
||||||
|
self._smbus_access(I2C_SMBUS_WRITE, register, I2C_SMBUS_BYTE_DATA, data)
|
||||||
|
|
||||||
|
def read_i2c_block_data(self, addr: int, register: int, length: int, force: bool = False) -> list[int]:
|
||||||
|
self._set_address(addr, force)
|
||||||
|
if not (0 <= length <= I2C_SMBUS_BLOCK_MAX):
|
||||||
|
raise ValueError(f"length must be 0..{I2C_SMBUS_BLOCK_MAX}")
|
||||||
|
|
||||||
|
data = _I2cSmbusData()
|
||||||
|
data.block[0] = length
|
||||||
|
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_I2C_BLOCK_DATA, data)
|
||||||
|
read_len = int(data.block[0]) or length
|
||||||
|
read_len = min(read_len, length)
|
||||||
|
return [int(b) for b in data.block[1 : read_len + 1]]
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define DEFAULT_MODEL "Dark Souls 2 (Default)"
|
#define DEFAULT_MODEL "CD210 (Default)"
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
||||||
{"TermsVersion", {PERSISTENT, STRING}},
|
{"TermsVersion", {PERSISTENT, STRING}},
|
||||||
|
{"TorqueBar", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"TrainingVersion", {PERSISTENT, STRING}},
|
{"TrainingVersion", {PERSISTENT, STRING}},
|
||||||
{"UbloxAvailable", {PERSISTENT, BOOL}},
|
{"UbloxAvailable", {PERSISTENT, BOOL}},
|
||||||
{"UpdateAvailable", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
{"UpdateAvailable", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||||
@@ -136,6 +137,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
||||||
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
|
{"BlinkerLateralReengageDelay", {PERSISTENT | BACKUP, INT, "0"}}, // seconds
|
||||||
{"BlinkerMinLateralControlSpeed", {PERSISTENT | BACKUP, INT, "20"}}, // MPH or km/h
|
{"BlinkerMinLateralControlSpeed", {PERSISTENT | BACKUP, INT, "20"}}, // MPH or km/h
|
||||||
{"BlinkerPauseLateralControl", {PERSISTENT | BACKUP, INT, "0"}},
|
{"BlinkerPauseLateralControl", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
{"Brightness", {PERSISTENT | BACKUP, INT, "0"}},
|
{"Brightness", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
@@ -168,12 +170,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
|
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
{"OnroadScreenOffControl", {PERSISTENT | BACKUP, BOOL}},
|
|
||||||
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
|
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
|
||||||
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||||
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
{"RocketFuel", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
@@ -248,7 +250,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"OsmStateTitle", {PERSISTENT, STRING}},
|
{"OsmStateTitle", {PERSISTENT, STRING}},
|
||||||
{"OsmWayTest", {PERSISTENT, STRING}},
|
{"OsmWayTest", {PERSISTENT, STRING}},
|
||||||
{"RoadName", {CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
{"RoadName", {CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
||||||
{"RoadNameToggle", {PERSISTENT, STRING}},
|
{"RoadNameToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|
||||||
// Speed Limit
|
// Speed Limit
|
||||||
{"SpeedLimitMode", {PERSISTENT | BACKUP, INT, "1"}},
|
{"SpeedLimitMode", {PERSISTENT | BACKUP, INT, "1"}},
|
||||||
|
|||||||
+3
-9
@@ -3,15 +3,9 @@ from numbers import Number
|
|||||||
|
|
||||||
class PIDController:
|
class PIDController:
|
||||||
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
||||||
self._k_p = k_p
|
self._k_p: list[list[float]] = [[0], [k_p]] if isinstance(k_p, Number) else k_p
|
||||||
self._k_i = k_i
|
self._k_i: list[list[float]] = [[0], [k_i]] if isinstance(k_i, Number) else k_i
|
||||||
self._k_d = k_d
|
self._k_d: list[list[float]] = [[0], [k_d]] if isinstance(k_d, Number) else k_d
|
||||||
if isinstance(self._k_p, Number):
|
|
||||||
self._k_p = [[0], [self._k_p]]
|
|
||||||
if isinstance(self._k_i, Number):
|
|
||||||
self._k_i = [[0], [self._k_i]]
|
|
||||||
if isinstance(self._k_d, Number):
|
|
||||||
self._k_d = [[0], [self._k_d]]
|
|
||||||
|
|
||||||
self.set_limits(pos_limit, neg_limit)
|
self.set_limits(pos_limit, neg_limit)
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -13,7 +13,11 @@ public:
|
|||||||
if (prefix.empty()) {
|
if (prefix.empty()) {
|
||||||
prefix = util::random_string(15);
|
prefix = util::random_string(15);
|
||||||
}
|
}
|
||||||
msgq_path = Path::shm_path() + "/" + prefix;
|
#ifdef __APPLE__
|
||||||
|
msgq_path = "/tmp/msgq_" + prefix;
|
||||||
|
#else
|
||||||
|
msgq_path = "/dev/shm/msgq_" + prefix;
|
||||||
|
#endif
|
||||||
bool ret = util::create_directories(msgq_path, 0777);
|
bool ret = util::create_directories(msgq_path, 0777);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
||||||
|
|||||||
+4
-2
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@@ -9,9 +10,10 @@ from openpilot.system.hardware.hw import Paths
|
|||||||
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
|
||||||
|
|
||||||
class OpenpilotPrefix:
|
class OpenpilotPrefix:
|
||||||
def __init__(self, prefix: str = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
def __init__(self, prefix: str | None = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False):
|
||||||
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
||||||
self.msgq_path = os.path.join(Paths.shm_path(), "msgq_" + self.prefix)
|
shm_path = "/tmp" if platform.system() == "Darwin" else "/dev/shm"
|
||||||
|
self.msgq_path = os.path.join(shm_path, "msgq_" + self.prefix)
|
||||||
self.create_dirs_on_enter = create_dirs_on_enter
|
self.create_dirs_on_enter = create_dirs_on_enter
|
||||||
self.clean_dirs_on_exit = clean_dirs_on_exit
|
self.clean_dirs_on_exit = clean_dirs_on_exit
|
||||||
self.shared_download_cache = shared_download_cache
|
self.shared_download_cache = shared_download_cache
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import time
|
|||||||
|
|
||||||
from setproctitle import getproctitle
|
from setproctitle import getproctitle
|
||||||
|
|
||||||
from openpilot.common.util import MovingAverage
|
from openpilot.common.utils import MovingAverage
|
||||||
from openpilot.system.hardware import PC
|
from openpilot.system.hardware import PC
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
Import('env', 'envCython')
|
|
||||||
|
|
||||||
transformations = env.Library('transformations', ['orientation.cc', 'coordinates.cc'])
|
|
||||||
transformations_python = envCython.Program('transformations.so', 'transformations.pyx')
|
|
||||||
Export('transformations', 'transformations_python')
|
|
||||||
@@ -102,3 +102,36 @@ class TestNED:
|
|||||||
np.testing.assert_allclose(converter.ned2ecef(ned_offsets_batch),
|
np.testing.assert_allclose(converter.ned2ecef(ned_offsets_batch),
|
||||||
ecef_positions_offset_batch,
|
ecef_positions_offset_batch,
|
||||||
rtol=1e-9, atol=1e-7)
|
rtol=1e-9, atol=1e-7)
|
||||||
|
|
||||||
|
def test_errors(self):
|
||||||
|
# Test wrong shape/type for geodetic2ecef
|
||||||
|
# numpy_wrap raises IndexError for scalar input
|
||||||
|
with np.testing.assert_raises(IndexError):
|
||||||
|
coord.geodetic2ecef(1.0)
|
||||||
|
|
||||||
|
with np.testing.assert_raises_regex(ValueError, "Geodetic must be size 3"):
|
||||||
|
coord.geodetic2ecef([0, 0])
|
||||||
|
|
||||||
|
with np.testing.assert_raises_regex(ValueError, "Geodetic must be size 3"):
|
||||||
|
coord.geodetic2ecef([0, 0, 0, 0])
|
||||||
|
|
||||||
|
with np.testing.assert_raises(TypeError):
|
||||||
|
coord.geodetic2ecef(['a', 'b', 'c'])
|
||||||
|
|
||||||
|
# Test LocalCoord constructor errors
|
||||||
|
with np.testing.assert_raises(ValueError):
|
||||||
|
coord.LocalCoord.from_geodetic([0, 0])
|
||||||
|
|
||||||
|
with np.testing.assert_raises(ValueError):
|
||||||
|
coord.LocalCoord.from_geodetic(1)
|
||||||
|
|
||||||
|
with np.testing.assert_raises(TypeError):
|
||||||
|
coord.LocalCoord.from_geodetic(['a', 'b', 'c'])
|
||||||
|
|
||||||
|
# Test wrong shape/type for ecef2geodetic
|
||||||
|
with np.testing.assert_raises(ValueError):
|
||||||
|
coord.ecef2geodetic([1, 2])
|
||||||
|
with np.testing.assert_raises(ValueError):
|
||||||
|
coord.ecef2geodetic([1, 2, 3, 4])
|
||||||
|
with np.testing.assert_raises(IndexError):
|
||||||
|
coord.ecef2geodetic(1.0)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
from openpilot.common.transformations.orientation import euler2quat, quat2euler, euler2rot, rot2euler, \
|
from openpilot.common.transformations.orientation import euler2quat, quat2euler, euler2rot, rot2euler, \
|
||||||
rot2quat, quat2rot, \
|
rot2quat, quat2rot, \
|
||||||
@@ -59,3 +60,32 @@ class TestOrientation:
|
|||||||
np.testing.assert_allclose(ned_eulers[i], ned_euler_from_ecef(ecef_positions[i], eulers[i]), rtol=1e-7)
|
np.testing.assert_allclose(ned_eulers[i], ned_euler_from_ecef(ecef_positions[i], eulers[i]), rtol=1e-7)
|
||||||
#np.testing.assert_allclose(eulers[i], ecef_euler_from_ned(ecef_positions[i], ned_eulers[i]), rtol=1e-7)
|
#np.testing.assert_allclose(eulers[i], ecef_euler_from_ned(ecef_positions[i], ned_eulers[i]), rtol=1e-7)
|
||||||
# np.testing.assert_allclose(ned_eulers, ned_euler_from_ecef(ecef_positions, eulers), rtol=1e-7)
|
# np.testing.assert_allclose(ned_eulers, ned_euler_from_ecef(ecef_positions, eulers), rtol=1e-7)
|
||||||
|
|
||||||
|
def test_inputs(self):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
euler2quat([1, 2])
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
quat2rot([1, 2, 3])
|
||||||
|
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
rot2quat(np.zeros((2, 2)))
|
||||||
|
|
||||||
|
def test_euler_rot_consistency(self):
|
||||||
|
rpy = [0.1, 0.2, 0.3]
|
||||||
|
R = euler2rot(rpy)
|
||||||
|
|
||||||
|
# R -> q -> R
|
||||||
|
q = rot2quat(R)
|
||||||
|
R_new = quat2rot(q)
|
||||||
|
np.testing.assert_allclose(R, R_new, atol=1e-15)
|
||||||
|
|
||||||
|
# q -> R -> Euler (quat2euler) -> R
|
||||||
|
rpy_new = quat2euler(q)
|
||||||
|
R_new2 = euler2rot(rpy_new)
|
||||||
|
np.testing.assert_allclose(R, R_new2, atol=1e-15)
|
||||||
|
|
||||||
|
# R -> Euler (rot2euler) -> R
|
||||||
|
rpy_from_rot = rot2euler(R)
|
||||||
|
R_new3 = euler2rot(rpy_from_rot)
|
||||||
|
np.testing.assert_allclose(R, R_new3, atol=1e-15)
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
# cython: language_level=3
|
|
||||||
from libcpp cimport bool
|
|
||||||
|
|
||||||
cdef extern from "orientation.cc":
|
|
||||||
pass
|
|
||||||
|
|
||||||
cdef extern from "orientation.hpp":
|
|
||||||
cdef cppclass Quaternion "Eigen::Quaterniond":
|
|
||||||
Quaternion()
|
|
||||||
Quaternion(double, double, double, double)
|
|
||||||
double w()
|
|
||||||
double x()
|
|
||||||
double y()
|
|
||||||
double z()
|
|
||||||
|
|
||||||
cdef cppclass Vector3 "Eigen::Vector3d":
|
|
||||||
Vector3()
|
|
||||||
Vector3(double, double, double)
|
|
||||||
double operator()(int)
|
|
||||||
|
|
||||||
cdef cppclass Matrix3 "Eigen::Matrix3d":
|
|
||||||
Matrix3()
|
|
||||||
Matrix3(double*)
|
|
||||||
|
|
||||||
double operator()(int, int)
|
|
||||||
|
|
||||||
Quaternion euler2quat(const Vector3 &)
|
|
||||||
Vector3 quat2euler(const Quaternion &)
|
|
||||||
Matrix3 quat2rot(const Quaternion &)
|
|
||||||
Quaternion rot2quat(const Matrix3 &)
|
|
||||||
Vector3 rot2euler(const Matrix3 &)
|
|
||||||
Matrix3 euler2rot(const Vector3 &)
|
|
||||||
Matrix3 rot_matrix(double, double, double)
|
|
||||||
Vector3 ecef_euler_from_ned(const ECEF &, const Vector3 &)
|
|
||||||
Vector3 ned_euler_from_ecef(const ECEF &, const Vector3 &)
|
|
||||||
|
|
||||||
|
|
||||||
cdef extern from "coordinates.cc":
|
|
||||||
cdef struct ECEF:
|
|
||||||
double x
|
|
||||||
double y
|
|
||||||
double z
|
|
||||||
|
|
||||||
cdef struct NED:
|
|
||||||
double n
|
|
||||||
double e
|
|
||||||
double d
|
|
||||||
|
|
||||||
cdef struct Geodetic:
|
|
||||||
double lat
|
|
||||||
double lon
|
|
||||||
double alt
|
|
||||||
bool radians
|
|
||||||
|
|
||||||
ECEF geodetic2ecef(const Geodetic &)
|
|
||||||
Geodetic ecef2geodetic(const ECEF &)
|
|
||||||
|
|
||||||
cdef cppclass LocalCoord_c "LocalCoord":
|
|
||||||
Matrix3 ned2ecef_matrix
|
|
||||||
Matrix3 ecef2ned_matrix
|
|
||||||
|
|
||||||
LocalCoord_c(const Geodetic &, const ECEF &)
|
|
||||||
LocalCoord_c(const Geodetic &)
|
|
||||||
LocalCoord_c(const ECEF &)
|
|
||||||
|
|
||||||
NED ecef2ned(const ECEF &)
|
|
||||||
ECEF ned2ecef(const NED &)
|
|
||||||
NED geodetic2ned(const Geodetic &)
|
|
||||||
Geodetic ned2geodetic(const NED &)
|
|
||||||
|
|
||||||
cdef extern from "coordinates.hpp":
|
|
||||||
pass
|
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
a = 6378137.0
|
||||||
|
b = 6356752.3142
|
||||||
|
esq = 6.69437999014e-3
|
||||||
|
e1sq = 6.73949674228e-3
|
||||||
|
|
||||||
|
|
||||||
|
def geodetic2ecef_single(g):
|
||||||
|
"""
|
||||||
|
Convert geodetic coordinates (latitude, longitude, altitude) to ECEF.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if len(g) != 3:
|
||||||
|
raise ValueError("Geodetic must be size 3")
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError("Geodetic must be a sequence of length 3") from None
|
||||||
|
|
||||||
|
lat, lon, alt = g
|
||||||
|
lat = np.radians(lat)
|
||||||
|
lon = np.radians(lon)
|
||||||
|
xi = np.sqrt(1.0 - esq * np.sin(lat)**2)
|
||||||
|
x = (a / xi + alt) * np.cos(lat) * np.cos(lon)
|
||||||
|
y = (a / xi + alt) * np.cos(lat) * np.sin(lon)
|
||||||
|
z = (a / xi * (1.0 - esq) + alt) * np.sin(lat)
|
||||||
|
return np.array([x, y, z])
|
||||||
|
|
||||||
|
|
||||||
|
def ecef2geodetic_single(e):
|
||||||
|
"""
|
||||||
|
Convert ECEF to geodetic coordinates using Ferrari's solution.
|
||||||
|
"""
|
||||||
|
x, y, z = e
|
||||||
|
r = np.sqrt(x**2 + y**2)
|
||||||
|
Esq = a**2 - b**2
|
||||||
|
F = 54 * b**2 * z**2
|
||||||
|
G = r**2 + (1 - esq) * z**2 - esq * Esq
|
||||||
|
C = (esq**2 * F * r**2) / (G**3)
|
||||||
|
S = np.cbrt(1 + C + np.sqrt(C**2 + 2 * C))
|
||||||
|
P = F / (3 * (S + 1 / S + 1)**2 * G**2)
|
||||||
|
Q = np.sqrt(1 + 2 * esq**2 * P)
|
||||||
|
r_0 = -(P * esq * r) / (1 + Q) + np.sqrt(0.5 * a**2 * (1 + 1.0 / Q) - P * (1 - esq) * z**2 / (Q * (1 + Q)) - 0.5 * P * r**2)
|
||||||
|
U = np.sqrt((r - esq * r_0)**2 + z**2)
|
||||||
|
V = np.sqrt((r - esq * r_0)**2 + (1 - esq) * z**2)
|
||||||
|
Z_0 = b**2 * z / (a * V)
|
||||||
|
h = U * (1 - b**2 / (a * V))
|
||||||
|
lat = np.arctan((z + e1sq * Z_0) / r)
|
||||||
|
lon = np.arctan2(y, x)
|
||||||
|
return np.array([np.degrees(lat), np.degrees(lon), h])
|
||||||
|
|
||||||
|
|
||||||
|
def euler2quat_single(euler):
|
||||||
|
"""
|
||||||
|
Convert Euler angles (roll, pitch, yaw) to a quaternion.
|
||||||
|
Rotation order: Z-Y-X (yaw, pitch, roll).
|
||||||
|
"""
|
||||||
|
phi, theta, psi = euler
|
||||||
|
|
||||||
|
c_phi, s_phi = np.cos(phi / 2), np.sin(phi / 2)
|
||||||
|
c_theta, s_theta = np.cos(theta / 2), np.sin(theta / 2)
|
||||||
|
c_psi, s_psi = np.cos(psi / 2), np.sin(psi / 2)
|
||||||
|
|
||||||
|
w = c_phi * c_theta * c_psi + s_phi * s_theta * s_psi
|
||||||
|
x = s_phi * c_theta * c_psi - c_phi * s_theta * s_psi
|
||||||
|
y = c_phi * s_theta * c_psi + s_phi * c_theta * s_psi
|
||||||
|
z = c_phi * c_theta * s_psi - s_phi * s_theta * c_psi
|
||||||
|
|
||||||
|
if w < 0:
|
||||||
|
return np.array([-w, -x, -y, -z])
|
||||||
|
return np.array([w, x, y, z])
|
||||||
|
|
||||||
|
|
||||||
|
def quat2euler_single(q):
|
||||||
|
"""
|
||||||
|
Convert a quaternion to Euler angles (roll, pitch, yaw).
|
||||||
|
"""
|
||||||
|
w, x, y, z = q
|
||||||
|
gamma = np.arctan2(2 * (w * x + y * z), 1 - 2 * (x**2 + y**2))
|
||||||
|
sin_arg = 2 * (w * y - z * x)
|
||||||
|
sin_arg = np.clip(sin_arg, -1.0, 1.0)
|
||||||
|
theta = np.arcsin(sin_arg)
|
||||||
|
psi = np.arctan2(2 * (w * z + x * y), 1 - 2 * (y**2 + z**2))
|
||||||
|
return np.array([gamma, theta, psi])
|
||||||
|
|
||||||
|
|
||||||
|
def quat2rot_single(q):
|
||||||
|
"""
|
||||||
|
Convert a quaternion to a 3x3 rotation matrix.
|
||||||
|
"""
|
||||||
|
w, x, y, z = q
|
||||||
|
xx, yy, zz = x * x, y * y, z * z
|
||||||
|
xy, xz, yz = x * y, x * z, y * z
|
||||||
|
wx, wy, wz = w * x, w * y, w * z
|
||||||
|
|
||||||
|
mat = np.array([
|
||||||
|
[1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy)],
|
||||||
|
[2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx)],
|
||||||
|
[2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy)]
|
||||||
|
])
|
||||||
|
return mat
|
||||||
|
|
||||||
|
|
||||||
|
def rot2quat_single(rot):
|
||||||
|
"""
|
||||||
|
Convert a 3x3 rotation matrix to a quaternion.
|
||||||
|
"""
|
||||||
|
trace = np.trace(rot)
|
||||||
|
if trace > 0:
|
||||||
|
s = 0.5 / np.sqrt(trace + 1.0)
|
||||||
|
w = 0.25 / s
|
||||||
|
x = (rot[2, 1] - rot[1, 2]) * s
|
||||||
|
y = (rot[0, 2] - rot[2, 0]) * s
|
||||||
|
z = (rot[1, 0] - rot[0, 1]) * s
|
||||||
|
else:
|
||||||
|
if rot[0, 0] > rot[1, 1] and rot[0, 0] > rot[2, 2]:
|
||||||
|
s = 2.0 * np.sqrt(1.0 + rot[0, 0] - rot[1, 1] - rot[2, 2])
|
||||||
|
w = (rot[2, 1] - rot[1, 2]) / s
|
||||||
|
x = 0.25 * s
|
||||||
|
y = (rot[0, 1] + rot[1, 0]) / s
|
||||||
|
z = (rot[0, 2] + rot[2, 0]) / s
|
||||||
|
elif rot[1, 1] > rot[2, 2]:
|
||||||
|
s = 2.0 * np.sqrt(1.0 + rot[1, 1] - rot[0, 0] - rot[2, 2])
|
||||||
|
w = (rot[0, 2] - rot[2, 0]) / s
|
||||||
|
x = (rot[0, 1] + rot[1, 0]) / s
|
||||||
|
y = 0.25 * s
|
||||||
|
z = (rot[1, 2] + rot[2, 1]) / s
|
||||||
|
else:
|
||||||
|
s = 2.0 * np.sqrt(1.0 + rot[2, 2] - rot[0, 0] - rot[1, 1])
|
||||||
|
w = (rot[1, 0] - rot[0, 1]) / s
|
||||||
|
x = (rot[0, 2] + rot[2, 0]) / s
|
||||||
|
y = (rot[1, 2] + rot[2, 1]) / s
|
||||||
|
z = 0.25 * s
|
||||||
|
|
||||||
|
if w < 0:
|
||||||
|
return np.array([-w, -x, -y, -z])
|
||||||
|
return np.array([w, x, y, z])
|
||||||
|
|
||||||
|
|
||||||
|
def euler2rot_single(euler):
|
||||||
|
"""
|
||||||
|
Convert Euler angles (roll, pitch, yaw) to a 3x3 rotation matrix.
|
||||||
|
Rotation order: Z-Y-X (yaw, pitch, roll).
|
||||||
|
"""
|
||||||
|
phi, theta, psi = euler
|
||||||
|
|
||||||
|
cx, sx = np.cos(phi), np.sin(phi)
|
||||||
|
cy, sy = np.cos(theta), np.sin(theta)
|
||||||
|
cz, sz = np.cos(psi), np.sin(psi)
|
||||||
|
|
||||||
|
Rx = np.array([[1, 0, 0], [0, cx, -sx], [0, sx, cx]])
|
||||||
|
Ry = np.array([[cy, 0, sy], [0, 1, 0], [-sy, 0, cy]])
|
||||||
|
Rz = np.array([[cz, -sz, 0], [sz, cz, 0], [0, 0, 1]])
|
||||||
|
|
||||||
|
return Rz @ Ry @ Rx
|
||||||
|
|
||||||
|
|
||||||
|
def rot2euler_single(rot):
|
||||||
|
"""
|
||||||
|
Convert a 3x3 rotation matrix to Euler angles (roll, pitch, yaw).
|
||||||
|
"""
|
||||||
|
return quat2euler_single(rot2quat_single(rot))
|
||||||
|
|
||||||
|
|
||||||
|
def rot_matrix(roll, pitch, yaw):
|
||||||
|
"""
|
||||||
|
Create a 3x3 rotation matrix from roll, pitch, and yaw angles.
|
||||||
|
"""
|
||||||
|
return euler2rot_single([roll, pitch, yaw])
|
||||||
|
|
||||||
|
|
||||||
|
def axis_angle_to_rot(axis, angle):
|
||||||
|
"""
|
||||||
|
Convert an axis-angle representation to a 3x3 rotation matrix.
|
||||||
|
"""
|
||||||
|
c = np.cos(angle / 2)
|
||||||
|
s = np.sin(angle / 2)
|
||||||
|
q = np.array([c, s*axis[0], s*axis[1], s*axis[2]])
|
||||||
|
return quat2rot_single(q)
|
||||||
|
|
||||||
|
|
||||||
|
class LocalCoord:
|
||||||
|
"""
|
||||||
|
A class to handle conversions between ECEF and local NED coordinates.
|
||||||
|
"""
|
||||||
|
def __init__(self, geodetic=None, ecef=None):
|
||||||
|
"""
|
||||||
|
Initialize LocalCoord with either geodetic or ECEF coordinates.
|
||||||
|
"""
|
||||||
|
if geodetic is not None:
|
||||||
|
self.init_ecef = geodetic2ecef_single(geodetic)
|
||||||
|
lat, lon, _ = geodetic
|
||||||
|
elif ecef is not None:
|
||||||
|
self.init_ecef = np.array(ecef)
|
||||||
|
lat, lon, _ = ecef2geodetic_single(ecef)
|
||||||
|
else:
|
||||||
|
raise ValueError("Must provide geodetic or ecef")
|
||||||
|
|
||||||
|
lat = np.radians(lat)
|
||||||
|
lon = np.radians(lon)
|
||||||
|
|
||||||
|
self.ned2ecef_matrix = np.array([
|
||||||
|
[-np.sin(lat) * np.cos(lon), -np.sin(lon), -np.cos(lat) * np.cos(lon)],
|
||||||
|
[-np.sin(lat) * np.sin(lon), np.cos(lon), -np.cos(lat) * np.sin(lon)],
|
||||||
|
[np.cos(lat), 0, -np.sin(lat)]
|
||||||
|
])
|
||||||
|
self.ecef2ned_matrix = self.ned2ecef_matrix.T
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_geodetic(cls, geodetic):
|
||||||
|
"""
|
||||||
|
Create a LocalCoord instance from geodetic coordinates.
|
||||||
|
"""
|
||||||
|
return cls(geodetic=geodetic)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_ecef(cls, ecef):
|
||||||
|
"""
|
||||||
|
Create a LocalCoord instance from ECEF coordinates.
|
||||||
|
"""
|
||||||
|
return cls(ecef=ecef)
|
||||||
|
|
||||||
|
def ecef2ned_single(self, ecef):
|
||||||
|
"""
|
||||||
|
Convert a single ECEF point to NED coordinates relative to the origin.
|
||||||
|
"""
|
||||||
|
return self.ecef2ned_matrix @ (ecef - self.init_ecef)
|
||||||
|
|
||||||
|
def ned2ecef_single(self, ned):
|
||||||
|
"""
|
||||||
|
Convert a single NED point to ECEF coordinates.
|
||||||
|
"""
|
||||||
|
return self.ned2ecef_matrix @ ned + self.init_ecef
|
||||||
|
|
||||||
|
def geodetic2ned_single(self, geodetic):
|
||||||
|
"""
|
||||||
|
Convert a single geodetic point to NED coordinates.
|
||||||
|
"""
|
||||||
|
ecef = geodetic2ecef_single(geodetic)
|
||||||
|
return self.ecef2ned_single(ecef)
|
||||||
|
|
||||||
|
def ned2geodetic_single(self, ned):
|
||||||
|
"""
|
||||||
|
Convert a single NED point to geodetic coordinates.
|
||||||
|
"""
|
||||||
|
ecef = self.ned2ecef_single(ned)
|
||||||
|
return ecef2geodetic_single(ecef)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ned_from_ecef_matrix(self):
|
||||||
|
"""
|
||||||
|
Returns the rotation matrix from ECEF to NED coordinates.
|
||||||
|
"""
|
||||||
|
return self.ecef2ned_matrix
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ecef_from_ned_matrix(self):
|
||||||
|
"""
|
||||||
|
Returns the rotation matrix from NED to ECEF coordinates.
|
||||||
|
"""
|
||||||
|
return self.ned2ecef_matrix
|
||||||
|
|
||||||
|
|
||||||
|
def ecef_euler_from_ned_single(ecef_init, ned_pose):
|
||||||
|
"""
|
||||||
|
Convert NED Euler angles (roll, pitch, yaw) at a given ECEF origin
|
||||||
|
to equivalent ECEF Euler angles.
|
||||||
|
"""
|
||||||
|
converter = LocalCoord(ecef=ecef_init)
|
||||||
|
zero = np.array(ecef_init)
|
||||||
|
|
||||||
|
x0 = converter.ned2ecef_single([1, 0, 0]) - zero
|
||||||
|
y0 = converter.ned2ecef_single([0, 1, 0]) - zero
|
||||||
|
z0 = converter.ned2ecef_single([0, 0, 1]) - zero
|
||||||
|
|
||||||
|
phi, theta, psi = ned_pose
|
||||||
|
|
||||||
|
x1 = axis_angle_to_rot(z0, psi) @ x0
|
||||||
|
y1 = axis_angle_to_rot(z0, psi) @ y0
|
||||||
|
z1 = axis_angle_to_rot(z0, psi) @ z0
|
||||||
|
|
||||||
|
x2 = axis_angle_to_rot(y1, theta) @ x1
|
||||||
|
y2 = axis_angle_to_rot(y1, theta) @ y1
|
||||||
|
z2 = axis_angle_to_rot(y1, theta) @ z1
|
||||||
|
|
||||||
|
x3 = axis_angle_to_rot(x2, phi) @ x2
|
||||||
|
y3 = axis_angle_to_rot(x2, phi) @ y2
|
||||||
|
|
||||||
|
x0 = np.array([1.0, 0, 0])
|
||||||
|
y0 = np.array([0, 1.0, 0])
|
||||||
|
z0 = np.array([0, 0, 1.0])
|
||||||
|
|
||||||
|
psi_out = np.arctan2(np.dot(x3, y0), np.dot(x3, x0))
|
||||||
|
theta_out = np.arctan2(-np.dot(x3, z0), np.sqrt(np.dot(x3, x0)**2 + np.dot(x3, y0)**2))
|
||||||
|
|
||||||
|
y2 = axis_angle_to_rot(z0, psi_out) @ y0
|
||||||
|
z2 = axis_angle_to_rot(y2, theta_out) @ z0
|
||||||
|
|
||||||
|
phi_out = np.arctan2(np.dot(y3, z2), np.dot(y3, y2))
|
||||||
|
|
||||||
|
return np.array([phi_out, theta_out, psi_out])
|
||||||
|
|
||||||
|
|
||||||
|
def ned_euler_from_ecef_single(ecef_init, ecef_pose):
|
||||||
|
"""
|
||||||
|
Convert ECEF Euler angles (roll, pitch, yaw) at a given ECEF origin
|
||||||
|
to equivalent NED Euler angles.
|
||||||
|
"""
|
||||||
|
converter = LocalCoord(ecef=ecef_init)
|
||||||
|
|
||||||
|
x0 = np.array([1.0, 0, 0])
|
||||||
|
y0 = np.array([0, 1.0, 0])
|
||||||
|
z0 = np.array([0, 0, 1.0])
|
||||||
|
|
||||||
|
phi, theta, psi = ecef_pose
|
||||||
|
|
||||||
|
x1 = axis_angle_to_rot(z0, psi) @ x0
|
||||||
|
y1 = axis_angle_to_rot(z0, psi) @ y0
|
||||||
|
z1 = axis_angle_to_rot(z0, psi) @ z0
|
||||||
|
|
||||||
|
x2 = axis_angle_to_rot(y1, theta) @ x1
|
||||||
|
y2 = axis_angle_to_rot(y1, theta) @ y1
|
||||||
|
z2 = axis_angle_to_rot(y1, theta) @ z1
|
||||||
|
|
||||||
|
x3 = axis_angle_to_rot(x2, phi) @ x2
|
||||||
|
y3 = axis_angle_to_rot(x2, phi) @ y2
|
||||||
|
|
||||||
|
zero = np.array(ecef_init)
|
||||||
|
x0 = converter.ned2ecef_single([1, 0, 0]) - zero
|
||||||
|
y0 = converter.ned2ecef_single([0, 1, 0]) - zero
|
||||||
|
z0 = converter.ned2ecef_single([0, 0, 1]) - zero
|
||||||
|
|
||||||
|
psi_out = np.arctan2(np.dot(x3, y0), np.dot(x3, x0))
|
||||||
|
theta_out = np.arctan2(-np.dot(x3, z0), np.sqrt(np.dot(x3, x0)**2 + np.dot(x3, y0)**2))
|
||||||
|
|
||||||
|
y2 = axis_angle_to_rot(z0, psi_out) @ y0
|
||||||
|
z2 = axis_angle_to_rot(y2, theta_out) @ z0
|
||||||
|
|
||||||
|
phi_out = np.arctan2(np.dot(y3, z2), np.dot(y3, y2))
|
||||||
|
|
||||||
|
return np.array([phi_out, theta_out, psi_out])
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
# distutils: language = c++
|
|
||||||
# cython: language_level = 3
|
|
||||||
from openpilot.common.transformations.transformations cimport Matrix3, Vector3, Quaternion
|
|
||||||
from openpilot.common.transformations.transformations cimport ECEF, NED, Geodetic
|
|
||||||
|
|
||||||
from openpilot.common.transformations.transformations cimport euler2quat as euler2quat_c
|
|
||||||
from openpilot.common.transformations.transformations cimport quat2euler as quat2euler_c
|
|
||||||
from openpilot.common.transformations.transformations cimport quat2rot as quat2rot_c
|
|
||||||
from openpilot.common.transformations.transformations cimport rot2quat as rot2quat_c
|
|
||||||
from openpilot.common.transformations.transformations cimport euler2rot as euler2rot_c
|
|
||||||
from openpilot.common.transformations.transformations cimport rot2euler as rot2euler_c
|
|
||||||
from openpilot.common.transformations.transformations cimport rot_matrix as rot_matrix_c
|
|
||||||
from openpilot.common.transformations.transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c
|
|
||||||
from openpilot.common.transformations.transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c
|
|
||||||
from openpilot.common.transformations.transformations cimport geodetic2ecef as geodetic2ecef_c
|
|
||||||
from openpilot.common.transformations.transformations cimport ecef2geodetic as ecef2geodetic_c
|
|
||||||
from openpilot.common.transformations.transformations cimport LocalCoord_c
|
|
||||||
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
cimport numpy as np
|
|
||||||
|
|
||||||
cdef np.ndarray[double, ndim=2] matrix2numpy(Matrix3 m):
|
|
||||||
return np.array([
|
|
||||||
[m(0, 0), m(0, 1), m(0, 2)],
|
|
||||||
[m(1, 0), m(1, 1), m(1, 2)],
|
|
||||||
[m(2, 0), m(2, 1), m(2, 2)],
|
|
||||||
])
|
|
||||||
|
|
||||||
cdef Matrix3 numpy2matrix(np.ndarray[double, ndim=2, mode="fortran"] m):
|
|
||||||
assert m.shape[0] == 3
|
|
||||||
assert m.shape[1] == 3
|
|
||||||
return Matrix3(<double*>m.data)
|
|
||||||
|
|
||||||
cdef ECEF list2ecef(ecef):
|
|
||||||
cdef ECEF e
|
|
||||||
e.x = ecef[0]
|
|
||||||
e.y = ecef[1]
|
|
||||||
e.z = ecef[2]
|
|
||||||
return e
|
|
||||||
|
|
||||||
cdef NED list2ned(ned):
|
|
||||||
cdef NED n
|
|
||||||
n.n = ned[0]
|
|
||||||
n.e = ned[1]
|
|
||||||
n.d = ned[2]
|
|
||||||
return n
|
|
||||||
|
|
||||||
cdef Geodetic list2geodetic(geodetic):
|
|
||||||
cdef Geodetic g
|
|
||||||
g.lat = geodetic[0]
|
|
||||||
g.lon = geodetic[1]
|
|
||||||
g.alt = geodetic[2]
|
|
||||||
return g
|
|
||||||
|
|
||||||
def euler2quat_single(euler):
|
|
||||||
cdef Vector3 e = Vector3(euler[0], euler[1], euler[2])
|
|
||||||
cdef Quaternion q = euler2quat_c(e)
|
|
||||||
return [q.w(), q.x(), q.y(), q.z()]
|
|
||||||
|
|
||||||
def quat2euler_single(quat):
|
|
||||||
cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3])
|
|
||||||
cdef Vector3 e = quat2euler_c(q)
|
|
||||||
return [e(0), e(1), e(2)]
|
|
||||||
|
|
||||||
def quat2rot_single(quat):
|
|
||||||
cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3])
|
|
||||||
cdef Matrix3 r = quat2rot_c(q)
|
|
||||||
return matrix2numpy(r)
|
|
||||||
|
|
||||||
def rot2quat_single(rot):
|
|
||||||
cdef Matrix3 r = numpy2matrix(np.asfortranarray(rot, dtype=np.double))
|
|
||||||
cdef Quaternion q = rot2quat_c(r)
|
|
||||||
return [q.w(), q.x(), q.y(), q.z()]
|
|
||||||
|
|
||||||
def euler2rot_single(euler):
|
|
||||||
cdef Vector3 e = Vector3(euler[0], euler[1], euler[2])
|
|
||||||
cdef Matrix3 r = euler2rot_c(e)
|
|
||||||
return matrix2numpy(r)
|
|
||||||
|
|
||||||
def rot2euler_single(rot):
|
|
||||||
cdef Matrix3 r = numpy2matrix(np.asfortranarray(rot, dtype=np.double))
|
|
||||||
cdef Vector3 e = rot2euler_c(r)
|
|
||||||
return [e(0), e(1), e(2)]
|
|
||||||
|
|
||||||
def rot_matrix(roll, pitch, yaw):
|
|
||||||
return matrix2numpy(rot_matrix_c(roll, pitch, yaw))
|
|
||||||
|
|
||||||
def ecef_euler_from_ned_single(ecef_init, ned_pose):
|
|
||||||
cdef ECEF init = list2ecef(ecef_init)
|
|
||||||
cdef Vector3 pose = Vector3(ned_pose[0], ned_pose[1], ned_pose[2])
|
|
||||||
|
|
||||||
cdef Vector3 e = ecef_euler_from_ned_c(init, pose)
|
|
||||||
return [e(0), e(1), e(2)]
|
|
||||||
|
|
||||||
def ned_euler_from_ecef_single(ecef_init, ecef_pose):
|
|
||||||
cdef ECEF init = list2ecef(ecef_init)
|
|
||||||
cdef Vector3 pose = Vector3(ecef_pose[0], ecef_pose[1], ecef_pose[2])
|
|
||||||
|
|
||||||
cdef Vector3 e = ned_euler_from_ecef_c(init, pose)
|
|
||||||
return [e(0), e(1), e(2)]
|
|
||||||
|
|
||||||
def geodetic2ecef_single(geodetic):
|
|
||||||
cdef Geodetic g = list2geodetic(geodetic)
|
|
||||||
cdef ECEF e = geodetic2ecef_c(g)
|
|
||||||
return [e.x, e.y, e.z]
|
|
||||||
|
|
||||||
def ecef2geodetic_single(ecef):
|
|
||||||
cdef ECEF e = list2ecef(ecef)
|
|
||||||
cdef Geodetic g = ecef2geodetic_c(e)
|
|
||||||
return [g.lat, g.lon, g.alt]
|
|
||||||
|
|
||||||
|
|
||||||
cdef class LocalCoord:
|
|
||||||
cdef LocalCoord_c * lc
|
|
||||||
|
|
||||||
def __init__(self, geodetic=None, ecef=None):
|
|
||||||
assert (geodetic is not None) or (ecef is not None)
|
|
||||||
if geodetic is not None:
|
|
||||||
self.lc = new LocalCoord_c(list2geodetic(geodetic))
|
|
||||||
elif ecef is not None:
|
|
||||||
self.lc = new LocalCoord_c(list2ecef(ecef))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ned2ecef_matrix(self):
|
|
||||||
return matrix2numpy(self.lc.ned2ecef_matrix)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ecef2ned_matrix(self):
|
|
||||||
return matrix2numpy(self.lc.ecef2ned_matrix)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ned_from_ecef_matrix(self):
|
|
||||||
return self.ecef2ned_matrix
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ecef_from_ned_matrix(self):
|
|
||||||
return self.ned2ecef_matrix
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_geodetic(cls, geodetic):
|
|
||||||
return cls(geodetic=geodetic)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_ecef(cls, ecef):
|
|
||||||
return cls(ecef=ecef)
|
|
||||||
|
|
||||||
def ecef2ned_single(self, ecef):
|
|
||||||
assert self.lc
|
|
||||||
cdef ECEF e = list2ecef(ecef)
|
|
||||||
cdef NED n = self.lc.ecef2ned(e)
|
|
||||||
return [n.n, n.e, n.d]
|
|
||||||
|
|
||||||
def ned2ecef_single(self, ned):
|
|
||||||
assert self.lc
|
|
||||||
cdef NED n = list2ned(ned)
|
|
||||||
cdef ECEF e = self.lc.ned2ecef(n)
|
|
||||||
return [e.x, e.y, e.z]
|
|
||||||
|
|
||||||
def geodetic2ned_single(self, geodetic):
|
|
||||||
assert self.lc
|
|
||||||
cdef Geodetic g = list2geodetic(geodetic)
|
|
||||||
cdef NED n = self.lc.geodetic2ned(g)
|
|
||||||
return [n.n, n.e, n.d]
|
|
||||||
|
|
||||||
def ned2geodetic_single(self, ned):
|
|
||||||
assert self.lc
|
|
||||||
cdef NED n = list2ned(ned)
|
|
||||||
cdef Geodetic g = self.lc.ned2geodetic(n)
|
|
||||||
return [g.lat, g.lon, g.alt]
|
|
||||||
|
|
||||||
def __dealloc__(self):
|
|
||||||
del self.lc
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
def sudo_write(val: str, path: str) -> None:
|
|
||||||
try:
|
|
||||||
with open(path, 'w') as f:
|
|
||||||
f.write(str(val))
|
|
||||||
except PermissionError:
|
|
||||||
os.system(f"sudo chmod a+w {path}")
|
|
||||||
try:
|
|
||||||
with open(path, 'w') as f:
|
|
||||||
f.write(str(val))
|
|
||||||
except PermissionError:
|
|
||||||
# fallback for debugfs files
|
|
||||||
os.system(f"sudo su -c 'echo {val} > {path}'")
|
|
||||||
|
|
||||||
def sudo_read(path: str) -> str:
|
|
||||||
try:
|
|
||||||
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
|
|
||||||
except Exception:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
class MovingAverage:
|
|
||||||
def __init__(self, window_size: int):
|
|
||||||
self.window_size: int = window_size
|
|
||||||
self.buffer: list[float] = [0.0] * window_size
|
|
||||||
self.index: int = 0
|
|
||||||
self.count: int = 0
|
|
||||||
self.sum: float = 0.0
|
|
||||||
|
|
||||||
def add_value(self, new_value: float):
|
|
||||||
# Update the sum: subtract the value being replaced and add the new value
|
|
||||||
self.sum -= self.buffer[self.index]
|
|
||||||
self.buffer[self.index] = new_value
|
|
||||||
self.sum += new_value
|
|
||||||
|
|
||||||
# Update the index in a circular manner
|
|
||||||
self.index = (self.index + 1) % self.window_size
|
|
||||||
|
|
||||||
# Track the number of added values (for partial windows)
|
|
||||||
self.count = min(self.count + 1, self.window_size)
|
|
||||||
|
|
||||||
def get_average(self) -> float:
|
|
||||||
if self.count == 0:
|
|
||||||
return float('nan')
|
|
||||||
return self.sum / self.count
|
|
||||||
+157
-3
@@ -7,14 +7,82 @@ import time
|
|||||||
import functools
|
import functools
|
||||||
from subprocess import Popen, PIPE, TimeoutExpired
|
from subprocess import Popen, PIPE, TimeoutExpired
|
||||||
import zstandard as zstd
|
import zstandard as zstd
|
||||||
from openpilot.common.swaglog import cloudlog
|
|
||||||
|
|
||||||
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
||||||
|
|
||||||
|
class Timer:
|
||||||
|
"""Simple lap timer for profiling sequential operations."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._start = self._lap = time.monotonic()
|
||||||
|
self._sections = {}
|
||||||
|
|
||||||
|
def lap(self, name):
|
||||||
|
now = time.monotonic()
|
||||||
|
self._sections[name] = now - self._lap
|
||||||
|
self._lap = now
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total(self):
|
||||||
|
return time.monotonic() - self._start
|
||||||
|
|
||||||
|
def fmt(self, duration):
|
||||||
|
parts = ", ".join(f"{k}={v:.2f}s" + (f" ({duration/v:.0f}x)" if k == 'render' and v > 0 else "") for k, v in self._sections.items())
|
||||||
|
total = self.total
|
||||||
|
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
|
||||||
|
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
|
||||||
|
|
||||||
|
def sudo_write(val: str, path: str) -> None:
|
||||||
|
try:
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(str(val))
|
||||||
|
except PermissionError:
|
||||||
|
os.system(f"sudo chmod a+w {path}")
|
||||||
|
try:
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(str(val))
|
||||||
|
except PermissionError:
|
||||||
|
# fallback for debugfs files
|
||||||
|
os.system(f"sudo su -c 'echo {val} > {path}'")
|
||||||
|
|
||||||
|
|
||||||
|
def sudo_read(path: str) -> str:
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class MovingAverage:
|
||||||
|
def __init__(self, window_size: int):
|
||||||
|
self.window_size: int = window_size
|
||||||
|
self.buffer: list[float] = [0.0] * window_size
|
||||||
|
self.index: int = 0
|
||||||
|
self.count: int = 0
|
||||||
|
self.sum: float = 0.0
|
||||||
|
|
||||||
|
def add_value(self, new_value: float):
|
||||||
|
# Update the sum: subtract the value being replaced and add the new value
|
||||||
|
self.sum -= self.buffer[self.index]
|
||||||
|
self.buffer[self.index] = new_value
|
||||||
|
self.sum += new_value
|
||||||
|
|
||||||
|
# Update the index in a circular manner
|
||||||
|
self.index = (self.index + 1) % self.window_size
|
||||||
|
|
||||||
|
# Track the number of added values (for partial windows)
|
||||||
|
self.count = min(self.count + 1, self.window_size)
|
||||||
|
|
||||||
|
def get_average(self) -> float:
|
||||||
|
if self.count == 0:
|
||||||
|
return float('nan')
|
||||||
|
return self.sum / self.count
|
||||||
|
|
||||||
|
|
||||||
class CallbackReader:
|
class CallbackReader:
|
||||||
"""Wraps a file, but overrides the read method to also
|
"""Wraps a file, but overrides the read method to also
|
||||||
call a callback function with the number of bytes read so far."""
|
call a callback function with the number of bytes read so far."""
|
||||||
|
|
||||||
def __init__(self, f, callback, *args):
|
def __init__(self, f, callback, *args):
|
||||||
self.f = f
|
self.f = f
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
@@ -99,6 +167,92 @@ def managed_proc(cmd: list[str], env: dict[str, str]):
|
|||||||
proc.kill()
|
proc.kill()
|
||||||
|
|
||||||
|
|
||||||
|
def tabulate(tabular_data, headers=(), tablefmt="simple", floatfmt="g", stralign="left", numalign=None):
|
||||||
|
rows = [list(row) for row in tabular_data]
|
||||||
|
|
||||||
|
def fmt(val):
|
||||||
|
if isinstance(val, str):
|
||||||
|
return val
|
||||||
|
if isinstance(val, (bool, int)):
|
||||||
|
return str(val)
|
||||||
|
try:
|
||||||
|
return format(val, floatfmt)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return str(val)
|
||||||
|
|
||||||
|
formatted = [[fmt(c) for c in row] for row in rows]
|
||||||
|
hdrs = [str(h) for h in headers] if headers else None
|
||||||
|
|
||||||
|
ncols = max((len(r) for r in formatted), default=0)
|
||||||
|
if hdrs:
|
||||||
|
ncols = max(ncols, len(hdrs))
|
||||||
|
if ncols == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
for r in formatted:
|
||||||
|
r.extend([""] * (ncols - len(r)))
|
||||||
|
if hdrs:
|
||||||
|
hdrs.extend([""] * (ncols - len(hdrs)))
|
||||||
|
|
||||||
|
widths = [0] * ncols
|
||||||
|
if hdrs:
|
||||||
|
for i in range(ncols):
|
||||||
|
widths[i] = len(hdrs[i])
|
||||||
|
for row in formatted:
|
||||||
|
for i in range(ncols):
|
||||||
|
widths[i] = max(widths[i], max(len(ln) for ln in row[i].split('\n')))
|
||||||
|
|
||||||
|
def _align(s, w):
|
||||||
|
if stralign == "center":
|
||||||
|
return s.center(w)
|
||||||
|
return s.ljust(w)
|
||||||
|
|
||||||
|
if tablefmt == "html":
|
||||||
|
parts = ["<table>"]
|
||||||
|
if hdrs:
|
||||||
|
parts.append("<thead>")
|
||||||
|
parts.append("<tr>" + "".join(f"<th>{h}</th>" for h in hdrs) + "</tr>")
|
||||||
|
parts.append("</thead>")
|
||||||
|
parts.append("<tbody>")
|
||||||
|
for row in formatted:
|
||||||
|
parts.append("<tr>" + "".join(f"<td>{c}</td>" for c in row) + "</tr>")
|
||||||
|
parts.append("</tbody>")
|
||||||
|
parts.append("</table>")
|
||||||
|
return "\n".join(parts)
|
||||||
|
|
||||||
|
if tablefmt == "simple_grid":
|
||||||
|
def _sep(left, mid, right):
|
||||||
|
return left + mid.join("\u2500" * (w + 2) for w in widths) + right
|
||||||
|
|
||||||
|
top, mid_sep, bot = _sep("\u250c", "\u252c", "\u2510"), _sep("\u251c", "\u253c", "\u2524"), _sep("\u2514", "\u2534", "\u2518")
|
||||||
|
|
||||||
|
def _fmt_row(cells):
|
||||||
|
split = [c.split('\n') for c in cells]
|
||||||
|
nlines = max(len(s) for s in split)
|
||||||
|
for s in split:
|
||||||
|
s.extend([""] * (nlines - len(s)))
|
||||||
|
return ["\u2502" + "\u2502".join(f" {_align(split[i][li], widths[i])} " for i in range(ncols)) + "\u2502" for li in range(nlines)]
|
||||||
|
|
||||||
|
lines = [top]
|
||||||
|
if hdrs:
|
||||||
|
lines.extend(_fmt_row(hdrs))
|
||||||
|
lines.append(mid_sep)
|
||||||
|
for ri, row in enumerate(formatted):
|
||||||
|
lines.extend(_fmt_row(row))
|
||||||
|
lines.append(mid_sep if ri < len(formatted) - 1 else bot)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
# simple
|
||||||
|
gap = " "
|
||||||
|
lines = []
|
||||||
|
if hdrs:
|
||||||
|
lines.append(gap.join(h.ljust(w) for h, w in zip(hdrs, widths, strict=True)))
|
||||||
|
lines.append(gap.join("-" * w for w in widths))
|
||||||
|
for row in formatted:
|
||||||
|
lines.append(gap.join(_align(row[i], widths[i]) for i in range(ncols)))
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
@@ -107,11 +261,11 @@ def retry(attempts=3, delay=1.0, ignore_failure=False):
|
|||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
cloudlog.exception(f"{func.__name__} failed, trying again")
|
print(f"{func.__name__} failed, trying again")
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
if ignore_failure:
|
if ignore_failure:
|
||||||
cloudlog.error(f"{func.__name__} failed after retry")
|
print(f"{func.__name__} failed after retry")
|
||||||
else:
|
else:
|
||||||
raise Exception(f"{func.__name__} failed after retry")
|
raise Exception(f"{func.__name__} failed after retry")
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define COMMA_VERSION "0.10.3"
|
#define COMMA_VERSION "0.10.4"
|
||||||
|
|||||||
+82
-88
@@ -4,28 +4,26 @@
|
|||||||
|
|
||||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||||
|
|
||||||
# 341 Supported Cars
|
# 335 Supported Cars
|
||||||
|
|
||||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
||||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2016-18">Buy Here</a></sub></details>|||
|
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2016-18">Buy Here</a></sub></details>|||
|
||||||
|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2019">Buy Here</a></sub></details>|||
|
|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2019">Buy Here</a></sub></details>|||
|
||||||
|Acura|MDX 2025|All except Type S|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura MDX 2025">Buy Here</a></sub></details>|||
|
|Acura|MDX 2025-26|All except Type S|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura MDX 2025-26">Buy Here</a></sub></details>|||
|
||||||
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2016-18">Buy Here</a></sub></details>|||
|
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2016-18">Buy Here</a></sub></details>|||
|
||||||
|Acura|RDX 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2019-21">Buy Here</a></sub></details>|||
|
|Acura|RDX 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Acura|TLX 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2021">Buy Here</a></sub></details>|||
|
|Acura|TLX 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2021">Buy Here</a></sub></details>|||
|
||||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 2014-19">Buy Here</a></sub></details>|||
|
|Acura|TLX 2025|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2025">Buy Here</a></sub></details>|||
|
||||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>|||
|
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 2014-19">Buy Here</a></sub></details>|||
|
||||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q2 2018">Buy Here</a></sub></details>|||
|
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q3 2019-24">Buy Here</a></sub></details>|||
|
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q2 2018">Buy Here</a></sub></details>|||
|
||||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi RS3 2018">Buy Here</a></sub></details>|||
|
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q3 2019-24">Buy Here</a></sub></details>|||
|
||||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi S3 2015-17">Buy Here</a></sub></details>|||
|
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi RS3 2018">Buy Here</a></sub></details>|||
|
||||||
|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi S3 2015-17">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV 2022-23">Buy Here</a></sub></details>|||
|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Bolt EV Non-ACC 2017|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV Non-ACC 2017">Buy Here</a></sub></details>|||
|
|
||||||
|Chevrolet|Bolt EV Non-ACC 2018-21|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV Non-ACC 2018-21">Buy Here</a></sub></details>|||
|
|
||||||
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Equinox 2019-22">Buy Here</a></sub></details>|||
|
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Equinox 2019-22">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Malibu Non-ACC 2016-23|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Malibu Non-ACC 2016-23">Buy Here</a></sub></details>|||
|
|
||||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Silverado 1500 2020-21">Buy Here</a></sub></details>|||
|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Silverado 1500 2020-21">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Trailblazer 2021-22">Buy Here</a></sub></details>|||
|
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Trailblazer 2021-22">Buy Here</a></sub></details>|||
|
||||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2017-18">Buy Here</a></sub></details>|||
|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2017-18">Buy Here</a></sub></details>|||
|
||||||
@@ -34,33 +32,33 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2017-18">Buy Here</a></sub></details>|||
|
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Chrysler|Pacifica Hybrid 2019-25|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2019-25">Buy Here</a></sub></details>|||
|
|Chrysler|Pacifica Hybrid 2019-25|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2019-25">Buy Here</a></sub></details>|||
|
||||||
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None|<a href="https://youtu.be/VT-i3yRsX2s?t=2736" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None|<a href="https://youtu.be/VT-i3yRsX2s?t=2736" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=CUPRA Ateca 2018-23">Buy Here</a></sub></details>|||
|
|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=CUPRA Ateca 2018-23">Buy Here</a></sub></details>|||
|
||||||
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Dodge Durango 2020-21">Buy Here</a></sub></details>|||
|
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Dodge Durango 2020-21">Buy Here</a></sub></details>|||
|
||||||
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Bronco Sport 2021-24">Buy Here</a></sub></details>|||
|
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Bronco Sport 2021-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2020-22">Buy Here</a></sub></details>|||
|
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Expedition 2022-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Expedition 2022-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer 2020-24">Buy Here</a></sub></details>|||
|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer Hybrid 2020-24">Buy Here</a></sub></details>|||
|
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer Hybrid 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 Hybrid 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 Hybrid 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Focus 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus 2018">Buy Here</a></sub></details>|||
|
|Ford|Focus 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus 2018">Buy Here</a></sub></details>|||
|
||||||
|Ford|Focus Hybrid 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus Hybrid 2018">Buy Here</a></sub></details>|||
|
|Ford|Focus Hybrid 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus Hybrid 2018">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga 2020-23">Buy Here</a></sub></details>|||
|
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2020-23">Buy Here</a></sub></details>|||
|
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Kuga Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2020-23">Buy Here</a></sub></details>|||
|
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2022">Buy Here</a></sub></details>|||
|
|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2022">Buy Here</a></sub></details>|||
|
||||||
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2023-24">Buy Here</a></sub></details>|||
|
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2023-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2022">Buy Here</a></sub></details>|||
|
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2022">Buy Here</a></sub></details>|||
|
||||||
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2023-24">Buy Here</a></sub></details>|||
|
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2023-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Mustang Mach-E 2021-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Mustang Mach-E 2021-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Mustang Mach-E 2021-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Mustang Mach-E 2021-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Ranger 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Ranger 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Genesis|G70 2018|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2018">Buy Here</a></sub></details>|||
|
|Genesis|G70 2018|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2018">Buy Here</a></sub></details>|||
|
||||||
|Genesis|G70 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2019-21">Buy Here</a></sub></details>|||
|
|Genesis|G70 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Genesis|G70 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2022-23">Buy Here</a></sub></details>|||
|
|Genesis|G70 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2022-23">Buy Here</a></sub></details>|||
|
||||||
@@ -90,12 +88,11 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Honda|Civic Hatchback Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid 2025-26">Buy Here</a></sub></details>|||
|
|Honda|Civic Hatchback Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid 2025-26">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid (Europe only) 2023">Buy Here</a></sub></details>|||
|
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid (Europe only) 2023">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hybrid 2025-26">Buy Here</a></sub></details>|||
|
|Honda|Civic Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hybrid 2025-26">Buy Here</a></sub></details>|||
|
||||||
|Honda|Clarity 2018-21|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector + Honda Clarity Proxy Board<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://shop.retropilot.org/product/honda-clarity-proxy-board-kit">Buy Here</a></sub></details>|||
|
|
||||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2015-16">Buy Here</a></sub></details>|||
|
|Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2015-16">Buy Here</a></sub></details>|||
|
||||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|15 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2017-22">Buy Here</a></sub></details>|||
|
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|15 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2017-22">Buy Here</a></sub></details>|||
|
||||||
|Honda|CR-V 2023-26|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2023-26">Buy Here</a></sub></details>|||
|
|Honda|CR-V 2023-26|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2023-26">Buy Here</a></sub></details>|||
|
||||||
|Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V Hybrid 2017-22">Buy Here</a></sub></details>|||
|
|Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V Hybrid 2017-22">Buy Here</a></sub></details>|||
|
||||||
|Honda|CR-V Hybrid 2023-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V Hybrid 2023-25">Buy Here</a></sub></details>|||
|
|Honda|CR-V Hybrid 2023-26|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V Hybrid 2023-26">Buy Here</a></sub></details>|||
|
||||||
|Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda e 2020">Buy Here</a></sub></details>|||
|
|Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda e 2020">Buy Here</a></sub></details>|||
|
||||||
|Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Fit 2018-20">Buy Here</a></sub></details>|||
|
|Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Fit 2018-20">Buy Here</a></sub></details>|||
|
||||||
|Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Freed 2020">Buy Here</a></sub></details>|||
|
|Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Freed 2020">Buy Here</a></sub></details>|||
|
||||||
@@ -106,6 +103,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Honda|N-Box 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|11 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda N-Box 2018">Buy Here</a></sub></details>|||
|
|Honda|N-Box 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|11 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda N-Box 2018">Buy Here</a></sub></details>|||
|
||||||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2018-20">Buy Here</a></sub></details>|||
|
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2018-20">Buy Here</a></sub></details>|||
|
||||||
|Honda|Odyssey 2021-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|43 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2021-26">Buy Here</a></sub></details>|||
|
|Honda|Odyssey 2021-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|43 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2021-26">Buy Here</a></sub></details>|||
|
||||||
|
|Honda|Odyssey (Taiwan) 2018-19|Honda Sensing|openpilot|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey (Taiwan) 2018-19">Buy Here</a></sub></details>|||
|
||||||
|Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2019-25">Buy Here</a></sub></details>|||
|
|Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2019-25">Buy Here</a></sub></details>|||
|
||||||
|Honda|Passport 2026|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2026">Buy Here</a></sub></details>|||
|
|Honda|Passport 2026|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2026">Buy Here</a></sub></details>|||
|
||||||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Pilot 2016-22">Buy Here</a></sub></details>|||
|
|Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Pilot 2016-22">Buy Here</a></sub></details>|||
|
||||||
@@ -120,7 +118,6 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra GT 2017-20">Buy Here</a></sub></details>|||
|
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra GT 2017-20">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Elantra Non-SCC 2022|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Non-SCC 2022">Buy Here</a></sub></details>|||
|
|
||||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Genesis 2015-16">Buy Here</a></sub></details>|||
|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Genesis 2015-16">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai i30 2017-19">Buy Here</a></sub></details>|||
|
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai i30 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24">Buy Here</a></sub></details>|||
|
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24">Buy Here</a></sub></details>|||
|
||||||
@@ -138,9 +135,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2018-21">Buy Here</a></sub></details>|||
|
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2018-21">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2022-23">Buy Here</a></sub></details>|||
|
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Kona Electric Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric Non-SCC 2019">Buy Here</a></sub></details>|||
|
|
||||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Hybrid 2020">Buy Here</a></sub></details>|||
|
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Hybrid 2020">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Kona Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Non-SCC 2019">Buy Here</a></sub></details>|||
|
|
||||||
|Hyundai|Nexo 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Nexo 2021">Buy Here</a></sub></details>|||
|
|Hyundai|Nexo 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Nexo 2021">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Cruz 2022-24">Buy Here</a></sub></details>|||
|
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Cruz 2022-24">Buy Here</a></sub></details>|||
|
||||||
@@ -164,13 +159,11 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival 2022-24">Buy Here</a></sub></details>|||
|
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival (China only) 2023">Buy Here</a></sub></details>|||
|
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival (China only) 2023">Buy Here</a></sub></details>|||
|
||||||
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed 2019-21">Buy Here</a></sub></details>|||
|
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Kia|Ceed Plug-in Hybrid Non-SCC 2022|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed Plug-in Hybrid Non-SCC 2022">Buy Here</a></sub></details>|||
|
|
||||||
|Kia|EV6 (Southeast Asia only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (Southeast Asia only) 2022-24">Buy Here</a></sub></details>|||
|
|Kia|EV6 (Southeast Asia only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (Southeast Asia only) 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|EV6 (with HDA II) 2022-24|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (with HDA II) 2022-24">Buy Here</a></sub></details>|||
|
|Kia|EV6 (with HDA II) 2022-24|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (with HDA II) 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (without HDA II) 2022-24">Buy Here</a></sub></details>|||
|
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (without HDA II) 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|6 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2019-21">Buy Here</a></sub></details>|||
|
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|6 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2022-23">Buy Here</a></sub></details>|||
|
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Kia|Forte Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte Non-SCC 2019">Buy Here</a></sub></details>|||
|
|
||||||
|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 2021-24">Buy Here</a></sub></details>|||
|
|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 2021-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
||||||
@@ -211,6 +204,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2017-19">Buy Here</a></sub></details>|||
|
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Lexus|IS 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2022-24">Buy Here</a></sub></details>|||
|
|Lexus|IS 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Lexus|LC 2024-25|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus LC 2024-25">Buy Here</a></sub></details>|||
|
|Lexus|LC 2024-25|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus LC 2024-25">Buy Here</a></sub></details>|||
|
||||||
|
|Lexus|LS 2018|All except Lexus Safety System+ A|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus LS 2018">Buy Here</a></sub></details>|||
|
||||||
|Lexus|NX 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus NX 2018-19">Buy Here</a></sub></details>|||
|
|Lexus|NX 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus NX 2018-19">Buy Here</a></sub></details>|||
|
||||||
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus NX 2020-21">Buy Here</a></sub></details>|||
|
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus NX 2020-21">Buy Here</a></sub></details>|||
|
||||||
|Lexus|NX Hybrid 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus NX Hybrid 2018-19">Buy Here</a></sub></details>|||
|
|Lexus|NX Hybrid 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus NX Hybrid 2018-19">Buy Here</a></sub></details>|||
|
||||||
@@ -226,21 +220,21 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus UX Hybrid 2019-24">Buy Here</a></sub></details>|||
|
|Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus UX Hybrid 2019-24">Buy Here</a></sub></details>|||
|
||||||
|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator 2020-24">Buy Here</a></sub></details>|||
|
|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator Plug-in Hybrid 2020-24">Buy Here</a></sub></details>|||
|
|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator Plug-in Hybrid 2020-24">Buy Here</a></sub></details>|||
|
||||||
|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN eTGE 2020-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN eTGE 2020-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN TGE 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN TGE 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-5 2022-25">Buy Here</a></sub></details>|||
|
|Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-5 2022-25">Buy Here</a></sub></details>|||
|
||||||
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|Altima 2019-20, 2024|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Altima 2019-20, 2024">Buy Here</a></sub></details>|||
|
|Nissan[<sup>5</sup>](#footnotes)|Altima 2019-20, 2024|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Altima 2019-20, 2024">Buy Here</a></sub></details>|||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Leaf 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Nissan[<sup>5</sup>](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Leaf 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Rogue 2018-20">Buy Here</a></sub></details>|||
|
|Nissan[<sup>5</sup>](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Rogue 2018-20">Buy Here</a></sub></details>|||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan X-Trail 2017">Buy Here</a></sub></details>|||
|
|Nissan[<sup>5</sup>](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan X-Trail 2017">Buy Here</a></sub></details>|||
|
||||||
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|32 mph|1 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 1500 2019-24">Buy Here</a></sub></details>|||
|
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|32 mph|1 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 1500 2019-24">Buy Here</a></sub></details>|||
|
||||||
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 2500 2020-24">Buy Here</a></sub></details>|||
|
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 2500 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 3500 2019-22">Buy Here</a></sub></details>|||
|
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 3500 2019-22">Buy Here</a></sub></details>|||
|
||||||
|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1S 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1S 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
||||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
||||||
|Subaru|Ascent 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Ascent 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
@@ -255,19 +249,19 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Subaru|Outback 2020-22|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2020-22|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Škoda|Fabia 2022-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
|Škoda|Fabia 2022-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Škoda|Kamiq 2021-23[<sup>11,13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kamiq 2021-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
|Škoda|Kamiq 2021-23[<sup>11,13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kamiq 2021-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Škoda|Karoq 2019-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Karoq 2019-23">Buy Here</a></sub></details>|||
|
|Škoda|Karoq 2019-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Karoq 2019-23">Buy Here</a></sub></details>|||
|
||||||
|Škoda|Kodiaq 2017-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kodiaq 2017-23">Buy Here</a></sub></details>|||
|
|Škoda|Kodiaq 2017-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kodiaq 2017-23">Buy Here</a></sub></details>|||
|
||||||
|Škoda|Octavia 2015-19[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia 2015-19">Buy Here</a></sub></details>|||
|
|Škoda|Octavia 2015-19[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia 2015-19">Buy Here</a></sub></details>|||
|
||||||
|Škoda|Octavia RS 2016[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia RS 2016">Buy Here</a></sub></details>|||
|
|Škoda|Octavia RS 2016[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia RS 2016">Buy Here</a></sub></details>|||
|
||||||
|Škoda|Octavia Scout 2017-19[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia Scout 2017-19">Buy Here</a></sub></details>|||
|
|Škoda|Octavia Scout 2017-19[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia Scout 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Škoda|Scala 2020-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Scala 2020-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
|Škoda|Scala 2020-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Scala 2020-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Škoda|Superb 2015-22[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Superb 2015-22">Buy Here</a></sub></details>|||
|
|Škoda|Superb 2015-22[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Superb 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW3) 2019-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW3) 2019-23">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW3) 2019-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW3) 2019-23">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW4) 2024-25[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla B connector<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW4) 2024-25">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW4) 2024-25[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla B connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW4) 2024-25">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model Y (with HW3) 2020-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model Y (with HW3) 2020-23">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model Y (with HW3) 2020-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model Y (with HW3) 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model Y (with HW4) 2024-25[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla B connector<br>- 1 USB-C coupler<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model Y (with HW4) 2024-25">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model Y (with HW4) 2024-25[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla B connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model Y (with HW4) 2024-25">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Alphard 2019-20">Buy Here</a></sub></details>|||
|
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Alphard 2019-20">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Alphard Hybrid 2021">Buy Here</a></sub></details>|||
|
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Alphard Hybrid 2021">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Avalon 2016|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Avalon 2016">Buy Here</a></sub></details>|||
|
|Toyota|Avalon 2016|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Avalon 2016">Buy Here</a></sub></details>|||
|
||||||
@@ -313,42 +307,42 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Toyota|RAV4 Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Toyota|RAV4 Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2023-25">Buy Here</a></sub></details>|<a href="https://youtu.be/4eIsEq4L4Ng" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2023-25">Buy Here</a></sub></details>|<a href="https://youtu.be/4eIsEq4L4Ng" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Sienna 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Toyota|Sienna 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon Shooting Brake 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon Shooting Brake 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas 2018-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas 2018-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas Cross Sport 2020-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas Cross Sport 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen California 2021-23">Buy Here</a></sub></details>|||
|
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen California 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Caravelle 2020">Buy Here</a></sub></details>|||
|
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Caravelle 2020">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Crafter 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Crafter 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Crafter 2018-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Crafter 2018-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Golf 2014-20">Buy Here</a></sub></details>|||
|
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Golf 2014-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf Alltrack 2015-19">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf Alltrack 2015-19">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTD 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTD 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTE 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTE 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTI 2015-21">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTI 2015-21">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf R 2015-19">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf R 2015-19">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf SportsVan 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf SportsVan 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Grand California 2019-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Grand California 2019-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta 2018-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Jetta 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta 2019-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta GLI 2021-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta GLI 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Passat 2015-22[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat 2015-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Passat 2015-22[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat Alltrack 2015-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat Alltrack 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat GTE 2015-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat GTE 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo 2018-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo 2018-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Cross 2021">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Cross 2021">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Roc 2018-23">Buy Here</a></sub></details>|||
|
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Roc 2018-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Taos 2022-24">Buy Here</a></sub></details>|||
|
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Taos 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont 2018-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont 2018-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont Cross Sport 2021-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont Cross Sport 2021-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont X 2021-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont X 2021-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan 2018-24">Buy Here</a></sub></details>|||
|
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan 2018-24">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan eHybrid 2021-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan eHybrid 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Touran 2016-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Touran 2016-23">Buy Here</a></sub></details>|||
|
||||||
|
|
||||||
### Footnotes
|
### Footnotes
|
||||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`. <br />
|
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`. <br />
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ export VECLIB_MAXIMUM_THREADS=1
|
|||||||
export QCOM_PRIORITY=12
|
export QCOM_PRIORITY=12
|
||||||
|
|
||||||
if [ -z "$AGNOS_VERSION" ]; then
|
if [ -z "$AGNOS_VERSION" ]; then
|
||||||
export AGNOS_VERSION="15.1"
|
export AGNOS_VERSION="16"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export STAGING_ROOT="/data/safe_staging"
|
export STAGING_ROOT="/data/safe_staging"
|
||||||
|
|||||||
+1
-1
Submodule msgq_repo updated: 6abe47bc98...4c4e814ed5
+1
-1
Submodule opendbc_repo updated: e03fbf9be8...383a720260
+1
-1
Submodule panda updated: 5f3c09c910...a95e060e85
+51
-77
@@ -1,11 +1,11 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "openpilot"
|
name = "openpilot"
|
||||||
requires-python = ">= 3.11, < 3.13"
|
requires-python = ">= 3.12.3, < 3.13"
|
||||||
license = {text = "MIT License"}
|
license = {text = "MIT License"}
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "an open source driver assistance system"
|
description = "an open source driver assistance system"
|
||||||
authors = [
|
authors = [
|
||||||
{name ="Vehicle Researcher", email="user@comma.ai"}
|
{name = "Vehicle Researcher", email="user@comma.ai"}
|
||||||
]
|
]
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -14,12 +14,9 @@ dependencies = [
|
|||||||
"pyserial", # pigeond + qcomgpsd
|
"pyserial", # pigeond + qcomgpsd
|
||||||
"requests", # many one-off uses
|
"requests", # many one-off uses
|
||||||
"sympy", # rednose + friends
|
"sympy", # rednose + friends
|
||||||
"crcmod", # cars + qcomgpsd
|
"crcmod-plus", # cars + qcomgpsd
|
||||||
"tqdm", # cars (fw_versions.py) on start + many one-off uses
|
"tqdm", # cars (fw_versions.py) on start + many one-off uses
|
||||||
|
|
||||||
# hardwared
|
|
||||||
"smbus2", # configuring amp
|
|
||||||
|
|
||||||
# core
|
# core
|
||||||
"cffi",
|
"cffi",
|
||||||
"scons",
|
"scons",
|
||||||
@@ -36,9 +33,6 @@ dependencies = [
|
|||||||
"pyopenssl < 24.3.0",
|
"pyopenssl < 24.3.0",
|
||||||
"pyaudio",
|
"pyaudio",
|
||||||
|
|
||||||
# ubloxd (TODO: just use struct)
|
|
||||||
"kaitaistruct",
|
|
||||||
|
|
||||||
# panda
|
# panda
|
||||||
"libusb1",
|
"libusb1",
|
||||||
"spidev; platform_system == 'Linux'",
|
"spidev; platform_system == 'Linux'",
|
||||||
@@ -49,7 +43,7 @@ dependencies = [
|
|||||||
# logging
|
# logging
|
||||||
"pyzmq",
|
"pyzmq",
|
||||||
"sentry-sdk",
|
"sentry-sdk",
|
||||||
"xattr", # used in place of 'os.getxattr' for macos compatibility
|
"xattr", # used in place of 'os.getxattr' for macOS compatibility
|
||||||
|
|
||||||
# athena
|
# athena
|
||||||
"PyJWT",
|
"PyJWT",
|
||||||
@@ -58,7 +52,6 @@ dependencies = [
|
|||||||
|
|
||||||
# acados deps
|
# acados deps
|
||||||
"casadi >=3.6.6", # 3.12 fixed in 3.6.6
|
"casadi >=3.6.6", # 3.12 fixed in 3.6.6
|
||||||
"future-fstrings",
|
|
||||||
|
|
||||||
# joystickd
|
# joystickd
|
||||||
"inputs",
|
"inputs",
|
||||||
@@ -72,32 +65,30 @@ dependencies = [
|
|||||||
"zstandard",
|
"zstandard",
|
||||||
|
|
||||||
# ui
|
# ui
|
||||||
"raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186
|
"raylib > 5.5.0.3",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"mapbox-earcut",
|
"mapbox-earcut",
|
||||||
|
"jeepney",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
docs = [
|
docs = [
|
||||||
"Jinja2",
|
"Jinja2",
|
||||||
"natsort",
|
|
||||||
"mkdocs",
|
"mkdocs",
|
||||||
]
|
]
|
||||||
|
|
||||||
testing = [
|
testing = [
|
||||||
"coverage",
|
"coverage",
|
||||||
"hypothesis ==6.47.*",
|
"hypothesis ==6.47.*",
|
||||||
"mypy",
|
"ty",
|
||||||
"pytest",
|
"pytest",
|
||||||
"pytest-cpp",
|
"pytest-cpp",
|
||||||
"pytest-subtests",
|
"pytest-subtests",
|
||||||
# https://github.com/pytest-dev/pytest-xdist/pull/1229
|
# https://github.com/pytest-dev/pytest-xdist/pull/1229
|
||||||
"pytest-xdist @ git+https://github.com/sshane/pytest-xdist@2b4372bd62699fb412c4fe2f95bf9f01bd2018da",
|
"pytest-xdist @ git+https://github.com/sshane/pytest-xdist@2b4372bd62699fb412c4fe2f95bf9f01bd2018da",
|
||||||
"pytest-timeout",
|
"pytest-timeout",
|
||||||
"pytest-randomly",
|
|
||||||
"pytest-asyncio",
|
"pytest-asyncio",
|
||||||
"pytest-mock",
|
"pytest-mock",
|
||||||
"pytest-repeat",
|
|
||||||
"ruff",
|
"ruff",
|
||||||
"codespell",
|
"codespell",
|
||||||
"pre-commit-hooks",
|
"pre-commit-hooks",
|
||||||
@@ -105,23 +96,12 @@ testing = [
|
|||||||
|
|
||||||
dev = [
|
dev = [
|
||||||
"av",
|
"av",
|
||||||
"azure-identity",
|
|
||||||
"azure-storage-blob",
|
|
||||||
"dbus-next", # TODO: remove once we moved everything to jeepney
|
|
||||||
"dictdiffer",
|
"dictdiffer",
|
||||||
"jeepney",
|
|
||||||
"matplotlib",
|
"matplotlib",
|
||||||
"opencv-python-headless",
|
"opencv-python-headless",
|
||||||
"parameterized >=0.8, <0.9",
|
"parameterized >=0.8, <0.9",
|
||||||
"pyautogui",
|
"pyautogui",
|
||||||
"pygame",
|
|
||||||
"pyopencl; platform_machine != 'aarch64'", # broken on arm64
|
|
||||||
"pytools>=2025.1.6; platform_machine != 'aarch64'",
|
|
||||||
"pywinctl",
|
"pywinctl",
|
||||||
"pyprof2calltree",
|
|
||||||
"tabulate",
|
|
||||||
"types-requests",
|
|
||||||
"types-tabulate",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
@@ -158,19 +138,9 @@ markers = [
|
|||||||
testpaths = [
|
testpaths = [
|
||||||
"common",
|
"common",
|
||||||
"selfdrive",
|
"selfdrive",
|
||||||
"system/manager",
|
"system",
|
||||||
"system/updated",
|
"tools",
|
||||||
"system/athena",
|
"cereal",
|
||||||
"system/camerad",
|
|
||||||
"system/hardware",
|
|
||||||
"system/loggerd",
|
|
||||||
"system/tests",
|
|
||||||
"system/ubloxd",
|
|
||||||
"system/webrtc",
|
|
||||||
"tools/lib/tests",
|
|
||||||
"tools/replay",
|
|
||||||
"tools/cabana",
|
|
||||||
"cereal/messaging/tests",
|
|
||||||
"sunnypilot",
|
"sunnypilot",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -181,43 +151,7 @@ ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,w
|
|||||||
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
||||||
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*, selfdrive/assets/offroad/mici_fcc.html"
|
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*, selfdrive/assets/offroad/mici_fcc.html"
|
||||||
|
|
||||||
[tool.mypy]
|
# https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml
|
||||||
python_version = "3.11"
|
|
||||||
exclude = [
|
|
||||||
"cereal/",
|
|
||||||
"msgq/",
|
|
||||||
"msgq_repo/",
|
|
||||||
"opendbc/",
|
|
||||||
"opendbc_repo/",
|
|
||||||
"panda/",
|
|
||||||
"rednose/",
|
|
||||||
"rednose_repo/",
|
|
||||||
"tinygrad/",
|
|
||||||
"tinygrad_repo/",
|
|
||||||
"teleoprtc/",
|
|
||||||
"teleoprtc_repo/",
|
|
||||||
"third_party/",
|
|
||||||
]
|
|
||||||
|
|
||||||
# third-party packages
|
|
||||||
ignore_missing_imports=true
|
|
||||||
|
|
||||||
# helpful warnings
|
|
||||||
warn_redundant_casts=true
|
|
||||||
warn_unreachable=true
|
|
||||||
warn_unused_ignores=true
|
|
||||||
|
|
||||||
# restrict dynamic typing
|
|
||||||
warn_return_any=true
|
|
||||||
|
|
||||||
# allow implicit optionals for default args
|
|
||||||
implicit_optional = true
|
|
||||||
|
|
||||||
local_partial_types=true
|
|
||||||
explicit_package_bases=true
|
|
||||||
disable_error_code = "annotation-unchecked"
|
|
||||||
|
|
||||||
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
indent-width = 2
|
indent-width = 2
|
||||||
lint.select = [
|
lint.select = [
|
||||||
@@ -275,3 +209,43 @@ lint.flake8-implicit-str-concat.allow-multiline = false
|
|||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
quote-style = "preserve"
|
quote-style = "preserve"
|
||||||
|
|
||||||
|
[tool.ty.src]
|
||||||
|
exclude = [
|
||||||
|
"cereal/",
|
||||||
|
"msgq/",
|
||||||
|
"msgq_repo/",
|
||||||
|
"opendbc/",
|
||||||
|
"opendbc_repo/",
|
||||||
|
"panda/",
|
||||||
|
"rednose/",
|
||||||
|
"rednose_repo/",
|
||||||
|
"tinygrad/",
|
||||||
|
"tinygrad_repo/",
|
||||||
|
"teleoprtc/",
|
||||||
|
"teleoprtc_repo/",
|
||||||
|
"third_party/",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ty.rules]
|
||||||
|
# Ignore unresolved imports for Cython-compiled modules (.pyx)
|
||||||
|
unresolved-import = "ignore"
|
||||||
|
# Ignore unresolved attributes - many from capnp and Cython modules
|
||||||
|
unresolved-attribute = "ignore"
|
||||||
|
# Ignore invalid method overrides - signature variance issues
|
||||||
|
invalid-method-override = "ignore"
|
||||||
|
# Ignore possibly-missing-attribute - too many false positives
|
||||||
|
possibly-missing-attribute = "ignore"
|
||||||
|
# Ignore invalid assignment - often intentional monkey-patching
|
||||||
|
invalid-assignment = "ignore"
|
||||||
|
# Ignore no-matching-overload - numpy/ctypes overload matching issues
|
||||||
|
no-matching-overload = "ignore"
|
||||||
|
# Ignore invalid-argument-type - many false positives from raylib, ctypes, numpy
|
||||||
|
invalid-argument-type = "ignore"
|
||||||
|
# Ignore call-non-callable - false positives from dynamic types
|
||||||
|
call-non-callable = "ignore"
|
||||||
|
# Ignore unsupported-operator - false positives from dynamic types
|
||||||
|
unsupported-operator = "ignore"
|
||||||
|
# Ignore not-subscriptable - false positives from dynamic types
|
||||||
|
not-subscriptable = "ignore"
|
||||||
|
# not-iterable errors are now fixed
|
||||||
|
|||||||
+8
-9
@@ -4,18 +4,17 @@
|
|||||||
## release checklist
|
## release checklist
|
||||||
|
|
||||||
### Go to staging
|
### Go to staging
|
||||||
- [ ] make a GitHub issue to track release
|
- [ ] make a GitHub issue to track release with this checklist
|
||||||
- [ ] create release master branch
|
- [ ] create release master branch
|
||||||
- [ ] update RELEASES.md
|
- [ ] create a branch from upstream master named `zerotentwo` for release `v0.10.2`
|
||||||
|
- [ ] revert risky commits (double check with autonomy team)
|
||||||
|
- [ ] push the new branch
|
||||||
|
- [ ] push to staging:
|
||||||
|
- [ ] make sure you are on the newly created release master branch (`zerotentwo`)
|
||||||
|
- [ ] run `BRANCH=devel-staging release/build_stripped.sh`. Jenkins will then automatically build staging on device, run `test_onroad` and update the staging branch
|
||||||
- [ ] bump version on master: `common/version.h` and `RELEASES.md`
|
- [ ] bump version on master: `common/version.h` and `RELEASES.md`
|
||||||
- [ ] build new userdata partition from `release3-staging`
|
|
||||||
- [ ] post on Discord, tag `@release crew`
|
- [ ] post on Discord, tag `@release crew`
|
||||||
|
|
||||||
Updating staging:
|
|
||||||
1. either rebase on master or cherry-pick changes
|
|
||||||
2. run this to update: `BRANCH=devel-staging release/build_devel.sh`
|
|
||||||
3. build new userdata partition from `release3-staging`
|
|
||||||
|
|
||||||
### Go to release
|
### Go to release
|
||||||
- [ ] before going to release, test the following:
|
- [ ] before going to release, test the following:
|
||||||
- [ ] update from previous release -> new release
|
- [ ] update from previous release -> new release
|
||||||
@@ -26,7 +25,7 @@ Updating staging:
|
|||||||
- [ ] check sentry, MTBF, etc.
|
- [ ] check sentry, MTBF, etc.
|
||||||
- [ ] stress test passes in production
|
- [ ] stress test passes in production
|
||||||
- [ ] publish the blog post
|
- [ ] publish the blog post
|
||||||
- [ ] `git reset --hard origin/release3-staging`
|
- [ ] `git reset --hard origin/release-mici-staging`
|
||||||
- [ ] tag the release: `git tag v0.X.X <commit-hash> && git push origin v0.X.X`
|
- [ ] tag the release: `git tag v0.X.X <commit-hash> && git push origin v0.X.X`
|
||||||
- [ ] create GitHub release
|
- [ ] create GitHub release
|
||||||
- [ ] final test install on `openpilot.comma.ai`
|
- [ ] final test install on `openpilot.comma.ai`
|
||||||
|
|||||||
@@ -68,8 +68,10 @@ def generate_metadata(model_path: Path, output_dir: Path, short_name: str):
|
|||||||
metadata_file = metadata_file.rename(output_path / f"{base}_{short_name.lower()}_metadata.pkl")
|
metadata_file = metadata_file.rename(output_path / f"{base}_{short_name.lower()}_metadata.pkl")
|
||||||
|
|
||||||
# Build the metadata structure
|
# Build the metadata structure
|
||||||
|
model_type = "offPolicy" if "off_policy" in base else base.split("_")[-1]
|
||||||
|
|
||||||
model_metadata = {
|
model_metadata = {
|
||||||
"type": base.split("_")[-1] if "dmonitoring" not in base else "dmonitoring",
|
"type": model_type,
|
||||||
"artifact": {
|
"artifact": {
|
||||||
"file_name": tinygrad_file.name,
|
"file_name": tinygrad_file.name,
|
||||||
"download_uri": {
|
"download_uri": {
|
||||||
|
|||||||
Executable
+212
@@ -0,0 +1,212 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Fetch CI results from GitHub Actions and Jenkins."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
JENKINS_URL = "https://jenkins.comma.life"
|
||||||
|
DEFAULT_TIMEOUT = 1800 # 30 minutes
|
||||||
|
POLL_INTERVAL = 30 # seconds
|
||||||
|
LOG_TAIL_LINES = 10 # lines of log to include for failed jobs
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_info():
|
||||||
|
branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], text=True).strip()
|
||||||
|
commit = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip()
|
||||||
|
return branch, commit
|
||||||
|
|
||||||
|
|
||||||
|
def get_github_actions_status(commit_sha):
|
||||||
|
result = subprocess.run(
|
||||||
|
["gh", "run", "list", "--commit", commit_sha, "--workflow", "tests.yaml", "--json", "databaseId,status,conclusion"],
|
||||||
|
capture_output=True, text=True, check=True
|
||||||
|
)
|
||||||
|
runs = json.loads(result.stdout)
|
||||||
|
if not runs:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
run_id = runs[0]["databaseId"]
|
||||||
|
result = subprocess.run(
|
||||||
|
["gh", "run", "view", str(run_id), "--json", "jobs"],
|
||||||
|
capture_output=True, text=True, check=True
|
||||||
|
)
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
jobs = {job["name"]: {"status": job["status"], "conclusion": job["conclusion"],
|
||||||
|
"duration": format_duration(job) if job["conclusion"] not in ("skipped", None) and job.get("startedAt") else "",
|
||||||
|
"id": job["databaseId"]}
|
||||||
|
for job in data.get("jobs", [])}
|
||||||
|
return jobs, run_id
|
||||||
|
|
||||||
|
|
||||||
|
def get_github_job_log(run_id, job_id):
|
||||||
|
result = subprocess.run(
|
||||||
|
["gh", "run", "view", str(run_id), "--job", str(job_id), "--log-failed"],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
lines = result.stdout.strip().split('\n')
|
||||||
|
return '\n'.join(lines[-LOG_TAIL_LINES:]) if len(lines) > LOG_TAIL_LINES else result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def format_duration(job):
|
||||||
|
start = datetime.fromisoformat(job["startedAt"].replace("Z", "+00:00"))
|
||||||
|
end = datetime.fromisoformat(job["completedAt"].replace("Z", "+00:00"))
|
||||||
|
secs = int((end - start).total_seconds())
|
||||||
|
return f"{secs // 60}m {secs % 60}s"
|
||||||
|
|
||||||
|
|
||||||
|
def get_jenkins_status(branch, commit_sha):
|
||||||
|
base_url = f"{JENKINS_URL}/job/openpilot/job/{branch}"
|
||||||
|
try:
|
||||||
|
# Get list of recent builds
|
||||||
|
with urllib.request.urlopen(f"{base_url}/api/json?tree=builds[number,url]", timeout=10) as resp:
|
||||||
|
builds = json.loads(resp.read().decode()).get("builds", [])
|
||||||
|
|
||||||
|
# Find build matching commit
|
||||||
|
for build in builds[:20]: # check last 20 builds
|
||||||
|
with urllib.request.urlopen(f"{build['url']}api/json", timeout=10) as resp:
|
||||||
|
data = json.loads(resp.read().decode())
|
||||||
|
for action in data.get("actions", []):
|
||||||
|
if action.get("_class") == "hudson.plugins.git.util.BuildData":
|
||||||
|
build_sha = action.get("lastBuiltRevision", {}).get("SHA1", "")
|
||||||
|
if build_sha.startswith(commit_sha) or commit_sha.startswith(build_sha):
|
||||||
|
# Get stages info
|
||||||
|
stages = []
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(f"{build['url']}wfapi/describe", timeout=10) as resp2:
|
||||||
|
wf_data = json.loads(resp2.read().decode())
|
||||||
|
stages = [{"name": s["name"], "status": s["status"]} for s in wf_data.get("stages", [])]
|
||||||
|
except urllib.error.HTTPError:
|
||||||
|
pass
|
||||||
|
return {
|
||||||
|
"number": data["number"],
|
||||||
|
"in_progress": data.get("inProgress", False),
|
||||||
|
"result": data.get("result"),
|
||||||
|
"url": data.get("url", ""),
|
||||||
|
"stages": stages,
|
||||||
|
}
|
||||||
|
return None # no build found for this commit
|
||||||
|
except urllib.error.HTTPError:
|
||||||
|
return None # branch doesn't exist on Jenkins
|
||||||
|
|
||||||
|
|
||||||
|
def get_jenkins_log(build_url):
|
||||||
|
url = f"{build_url}consoleText"
|
||||||
|
with urllib.request.urlopen(url, timeout=30) as resp:
|
||||||
|
text = resp.read().decode(errors='replace')
|
||||||
|
lines = text.strip().split('\n')
|
||||||
|
return '\n'.join(lines[-LOG_TAIL_LINES:]) if len(lines) > LOG_TAIL_LINES else text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def is_complete(gh_status, jenkins_status):
|
||||||
|
gh_done = gh_status is None or all(j["status"] == "completed" for j in gh_status.values())
|
||||||
|
jenkins_done = jenkins_status is None or not jenkins_status.get("in_progress", True)
|
||||||
|
return gh_done and jenkins_done
|
||||||
|
|
||||||
|
|
||||||
|
def status_icon(status, conclusion=None):
|
||||||
|
if status == "completed":
|
||||||
|
return ":white_check_mark:" if conclusion == "success" else ":x:"
|
||||||
|
return ":hourglass:" if status == "in_progress" else ":grey_question:"
|
||||||
|
|
||||||
|
|
||||||
|
def format_markdown(gh_status, gh_run_id, jenkins_status, commit_sha, branch):
|
||||||
|
lines = ["# CI Results", "",
|
||||||
|
f"**Branch**: {branch}",
|
||||||
|
f"**Commit**: {commit_sha[:7]}",
|
||||||
|
f"**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", ""]
|
||||||
|
|
||||||
|
lines.extend(["## GitHub Actions", "", "| Job | Status | Duration |", "|-----|--------|----------|"])
|
||||||
|
failed_gh_jobs = []
|
||||||
|
if gh_status:
|
||||||
|
for job_name, job in gh_status.items():
|
||||||
|
icon = status_icon(job["status"], job.get("conclusion"))
|
||||||
|
conclusion = job.get("conclusion") or job["status"]
|
||||||
|
lines.append(f"| {job_name} | {icon} {conclusion} | {job.get('duration', '')} |")
|
||||||
|
if job.get("conclusion") == "failure":
|
||||||
|
failed_gh_jobs.append((job_name, job.get("id")))
|
||||||
|
else:
|
||||||
|
lines.append("| - | No workflow runs found | |")
|
||||||
|
|
||||||
|
lines.extend(["", "## Jenkins", "", "| Stage | Status |", "|-------|--------|"])
|
||||||
|
failed_jenkins_stages = []
|
||||||
|
if jenkins_status:
|
||||||
|
stages = jenkins_status.get("stages", [])
|
||||||
|
if stages:
|
||||||
|
for stage in stages:
|
||||||
|
icon = ":white_check_mark:" if stage["status"] == "SUCCESS" else (
|
||||||
|
":x:" if stage["status"] == "FAILED" else ":hourglass:")
|
||||||
|
lines.append(f"| {stage['name']} | {icon} {stage['status'].lower()} |")
|
||||||
|
if stage["status"] == "FAILED":
|
||||||
|
failed_jenkins_stages.append(stage["name"])
|
||||||
|
# Show overall build status if still in progress
|
||||||
|
if jenkins_status["in_progress"]:
|
||||||
|
lines.append("| (build in progress) | :hourglass: in_progress |")
|
||||||
|
else:
|
||||||
|
icon = ":hourglass:" if jenkins_status["in_progress"] else (
|
||||||
|
":white_check_mark:" if jenkins_status["result"] == "SUCCESS" else ":x:")
|
||||||
|
status = "in progress" if jenkins_status["in_progress"] else (jenkins_status["result"] or "unknown")
|
||||||
|
lines.append(f"| #{jenkins_status['number']} | {icon} {status.lower()} |")
|
||||||
|
if jenkins_status.get("url"):
|
||||||
|
lines.append(f"\n[View build]({jenkins_status['url']})")
|
||||||
|
else:
|
||||||
|
lines.append("| - | No builds found for branch |")
|
||||||
|
|
||||||
|
if failed_gh_jobs or failed_jenkins_stages:
|
||||||
|
lines.extend(["", "## Failure Logs", ""])
|
||||||
|
|
||||||
|
for job_name, job_id in failed_gh_jobs:
|
||||||
|
lines.append(f"### GitHub Actions: {job_name}")
|
||||||
|
log = get_github_job_log(gh_run_id, job_id)
|
||||||
|
lines.extend(["", "```", log, "```", ""])
|
||||||
|
|
||||||
|
for stage_name in failed_jenkins_stages:
|
||||||
|
lines.append(f"### Jenkins: {stage_name}")
|
||||||
|
log = get_jenkins_log(jenkins_status["url"])
|
||||||
|
lines.extend(["", "```", log, "```", ""])
|
||||||
|
|
||||||
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Fetch CI results from GitHub Actions and Jenkins")
|
||||||
|
parser.add_argument("--wait", action="store_true", help="Wait for CI to complete")
|
||||||
|
parser.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT, help="Timeout in seconds (default: 1800)")
|
||||||
|
parser.add_argument("-o", "--output", default="ci_results.md", help="Output file (default: ci_results.md)")
|
||||||
|
parser.add_argument("--branch", help="Branch to check (default: current branch)")
|
||||||
|
parser.add_argument("--commit", help="Commit SHA to check (default: HEAD)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
branch, commit = get_git_info()
|
||||||
|
branch = args.branch or branch
|
||||||
|
commit = args.commit or commit
|
||||||
|
print(f"Fetching CI results for {branch} @ {commit[:7]}")
|
||||||
|
|
||||||
|
start_time = time.monotonic()
|
||||||
|
while True:
|
||||||
|
gh_status, gh_run_id = get_github_actions_status(commit)
|
||||||
|
jenkins_status = get_jenkins_status(branch, commit) if branch != "HEAD" else None
|
||||||
|
|
||||||
|
if not args.wait or is_complete(gh_status, jenkins_status):
|
||||||
|
break
|
||||||
|
|
||||||
|
elapsed = time.monotonic() - start_time
|
||||||
|
if elapsed >= args.timeout:
|
||||||
|
print(f"Timeout after {int(elapsed)}s")
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"CI still running, waiting {POLL_INTERVAL}s... ({int(elapsed)}s elapsed)")
|
||||||
|
time.sleep(POLL_INTERVAL)
|
||||||
|
|
||||||
|
content = format_markdown(gh_status, gh_run_id, jenkins_status, commit, branch)
|
||||||
|
with open(args.output, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print(f"Results written to {args.output}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -55,7 +55,7 @@ function run_tests() {
|
|||||||
run "check_nomerge_comments" $DIR/check_nomerge_comments.sh $ALL_FILES
|
run "check_nomerge_comments" $DIR/check_nomerge_comments.sh $ALL_FILES
|
||||||
|
|
||||||
if [[ -z "$FAST" ]]; then
|
if [[ -z "$FAST" ]]; then
|
||||||
run "mypy" mypy $PYTHON_FILES
|
run "ty" ty check
|
||||||
run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore
|
run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ function help() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e "${BOLD}${UNDERLINE}Tests:${NC}"
|
echo -e "${BOLD}${UNDERLINE}Tests:${NC}"
|
||||||
echo -e " ${BOLD}ruff${NC}"
|
echo -e " ${BOLD}ruff${NC}"
|
||||||
echo -e " ${BOLD}mypy${NC}"
|
echo -e " ${BOLD}ty${NC}"
|
||||||
echo -e " ${BOLD}codespell${NC}"
|
echo -e " ${BOLD}codespell${NC}"
|
||||||
echo -e " ${BOLD}check_added_large_files${NC}"
|
echo -e " ${BOLD}check_added_large_files${NC}"
|
||||||
echo -e " ${BOLD}check_shebang_scripts_are_executable${NC}"
|
echo -e " ${BOLD}check_shebang_scripts_are_executable${NC}"
|
||||||
@@ -81,11 +81,11 @@ function help() {
|
|||||||
echo " Specify tests to skip separated by spaces"
|
echo " Specify tests to skip separated by spaces"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${BOLD}${UNDERLINE}Examples:${NC}"
|
echo -e "${BOLD}${UNDERLINE}Examples:${NC}"
|
||||||
echo " op lint mypy ruff"
|
echo " op lint ty ruff"
|
||||||
echo " Only run the mypy and ruff tests"
|
echo " Only run the ty and ruff tests"
|
||||||
echo ""
|
echo ""
|
||||||
echo " op lint --skip mypy ruff"
|
echo " op lint --skip ty ruff"
|
||||||
echo " Skip the mypy and ruff tests"
|
echo " Skip the ty and ruff tests"
|
||||||
echo ""
|
echo ""
|
||||||
echo " op lint"
|
echo " op lint"
|
||||||
echo " Run all the tests"
|
echo " Run all the tests"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ LANGUAGES_FILE = TRANSLATIONS_DIR / "languages.json"
|
|||||||
|
|
||||||
GLYPH_PADDING = 6
|
GLYPH_PADDING = 6
|
||||||
EXTRA_CHARS = "–‑✓×°§•X⚙✕◀▶✔⌫⇧␣○●↳çêüñ–‑✓×°§•€£¥"
|
EXTRA_CHARS = "–‑✓×°§•X⚙✕◀▶✔⌫⇧␣○●↳çêüñ–‑✓×°§•€£¥"
|
||||||
UNIFONT_LANGUAGES = {"ar", "th", "zh-CHT", "zh-CHS", "ko", "ja"}
|
UNIFONT_LANGUAGES = {"th", "zh-CHT", "zh-CHS", "ko", "ja"}
|
||||||
|
|
||||||
|
|
||||||
def _languages():
|
def _languages():
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:263598da73c577c01cebd31ae78f45969ef8b335be1a5f55d54a696bb2982c0a
|
||||||
|
size 2062
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:9df44871e9f5fa910622b0b92205b92a54d137dbdc3827b92e8622d85ff2e08e
|
|
||||||
size 5189
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:013b368b38b17d9b2ef6aaf0f498f672deed95888084b7287f42bdfba617cbb6
|
|
||||||
size 10142
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:8fd563eec78d5ce4a8204c2f596789e1090cb3e26a35b4ffeacee4ab61968538
|
|
||||||
size 8303
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:0be8d5eddcd9f87acbf1daccf446be6218522120f64aee1ee0a3c0b31560f076
|
|
||||||
size 15761
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:613af9ed79bb26c60fbd19c094214f0881736c0e293f6d000b530cde0478a273
|
oid sha256:89ac033d879beeb0a7fa1919838e0ec64b1a625a4aafc14f7b990c607a79b676
|
||||||
size 2470
|
size 2220
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b77579c099c688d1a27f356197fba9c2c8efcf4d391af580b4b29f0e70587919
|
oid sha256:254b7f753b70c964847b686f0f71af751f2f49beea6ede4aeb333fe06062a257
|
||||||
size 2086
|
size 2289
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:eb42b8d6259238beb26f286dc28fb2dc8d91b00fec1f7a7655296b5769439a15
|
oid sha256:01841b602632c66ab14a8e52b874a1623f09641dc2ef0620f4e2d00bb4a913f3
|
||||||
size 15690
|
size 16243
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:17b6fe530598cbad34bcf31d4f21f929b792aacedef51b3ffef1941c86017811
|
oid sha256:744dbaa68ee74e300cd46439bad79449c860e1c5c027304b0f382bd5383fba77
|
||||||
size 7331
|
size 6817
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:05f3626e790622a4ad90e982c4aacb612d0785a752339352a3187addf763e2e9
|
oid sha256:3b11ee84d48972a2499cb29f01594d77a1a39692f6424a315a3f83262bc16087
|
||||||
size 13288
|
size 13481
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a877882a8dccb884bd35918f9f9b427a724a59e90a638e54f6fd5d0680ad173c
|
oid sha256:d548405a65ba4d4590c55866612dc6aa0e78d9278fc864ef60fe3e463edf4a68
|
||||||
size 12137
|
size 12169
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ba944b208abed9b8b9752adb8017bd29cd2e98c89fb07ee5d0a595185c7564a5
|
oid sha256:b6fc63326d34fbe72f6daf104d101ce19e547dbfe134427c067c957a7179df74
|
||||||
size 11898
|
size 12124
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a23743d21bc8160e013625210654a55634e4ed58e60057b70e08761bac1c3680
|
oid sha256:77b20a8c478d982412d556afb3a035b80b4aa9fe7a86aea761af4a42147d9435
|
||||||
size 40406
|
size 45297
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:acbfa3e38f0b9f422f5c1335ce20013852df2892b813db176a51918adc83ad58
|
oid sha256:584cea202afff6dd20d67ae1a9cd6d2b8cc07598bccb91a8d1bac0142567308e
|
||||||
size 40979
|
size 45489
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e0d00d743b01c49c2b739127e9916a229caf8c48346d6d168863b080ddcaa409
|
oid sha256:fd91685bf656e828648acf035a4737acb2c4709e8514cf0aa0a10fa470a9bb60
|
||||||
size 11124
|
size 11580
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f3f57346a1cf9a66f9fd746f87bcebb23b7a403e9d6e4fd7701b126abcdd47ea
|
||||||
|
size 18476
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b7eb870d01e5bf6c421e204026a4ea08e177731f2d6b5b17c4ad43c90c1c3e78
|
oid sha256:cb89d9f11cf44992f92142aa5ad84e1ac700a2601aff2abab373e2a822af149e
|
||||||
size 23549
|
size 11678
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f7b3bb76ee2359076339285ea6bced5b680e5b919a1b7dee163f36cd819c9ea1
|
oid sha256:e2772c6a9fe9c57099d347ad49f0cb7c906593f1fdf0e6dde96d104baf0200b0
|
||||||
size 1746
|
size 1365
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:51af75afbaf30abeaae1c99c7ad3e25cf5d5c90a2d6c799aad353b3302384b0a
|
oid sha256:07310879d093108435c0011846ae1184966db86443bc6e7ca036a6fa6123700b
|
||||||
size 4829
|
size 4983
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:88b2ecf3a9834d2b156bb632ec2090d7dc112e8ab61711ba645c03489d1c457f
|
oid sha256:7be447e56d649e0362ef650494b484e140a01ead31799ce43b266f5781c918d2
|
||||||
size 29157
|
size 36473
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:28c95c8970648d40b35b94724936a9ab7a6f4cbca367a40f01b86f9abedc70e5
|
oid sha256:56de402482b5987ed9a0ff3f793a1c89f857304b34fbb8a3deb5b5d4a332be1c
|
||||||
size 1587
|
size 3688
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d2a2cb4db429467783d7f721ffbed7838551e4aabf32771e73759c87b4a67bca
|
oid sha256:2aa6d04ba038f15a92868de6e6c7b04f624b4fe89d03bc3e9c4cd44cb729b24e
|
||||||
size 28880
|
size 38317
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0e845a211cf5d03f781efdd6eec4f8106e8dd85799ea59b51834a9099b479141
|
oid sha256:f9f7d0554c0c79ab605c1119ffdef0a4f55196e53b75a65b6ac5218911e24a02
|
||||||
size 30348
|
size 45701
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:009005539f14acc29a4f5510b4e9531d2ba3667133644f6e0069c12b08ba0fd9
|
oid sha256:7fae4872ab3c24d5e4c2be6150127a844f89bbdcadfccdff2dfed180e125d577
|
||||||
size 35370
|
size 45699
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:38a52171bdc6feb3ddfd2d9f9e59db3dabd09fa0aafbc9f81137c59bd03b7c26
|
oid sha256:14b457d2dc19d8658f525cc6989c9cfcf0edaf695b18767514242acbdbe2a6dd
|
||||||
size 2321
|
size 2198
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:10f469a6f5d25d9e2b0b1aae51b4fbd06d2c7b8417613bb321c2a30bb7298dab
|
oid sha256:7ad4ee47ec6470f788a026f95ed86bf344f64f9cf3186c9c78927233d2694a1d
|
||||||
size 1392
|
size 1388
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c655994336b7da4ca986c6f27494bcab66e77f016ec9db8df271de53ed93e517
|
oid sha256:b26133bee089627202d5e89a4e939ad23aaceb5d8e26d7381b1aea3ef892f2ee
|
||||||
size 1328
|
size 2620
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a1f058c5640bd763d2f6927432a1daff1587770ea0d06f2e351a28462e9d8335
|
oid sha256:ebb4f7ad9fd2f9fb3c69a38fbc00cbe690809b0ff202ffd4768ae5b699acc035
|
||||||
size 1743
|
size 1759
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:77a1281979f0b50f0e109ead56a88a33b81ef5901dd1a4537eb3fa048e0d90de
|
oid sha256:5f47e636025e044977f278a35546e0fc971f48fd53c2eeafd3508e95c35f378f
|
||||||
size 1345
|
size 3117
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2649d36259700d32a0edef878647e76492b1bec2fe34ac8ea806d4e7e4c57855
|
oid sha256:66858a5d3302333485fa391f7a9bb3a9b1ab4ae881e7fb47b04c3a4507011c94
|
||||||
size 2668
|
size 2613
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4b982ac1b78b45487490d1dbbffed1f68735f6a35def502e882f706c30683aff
|
oid sha256:f646263b26de46f79cac836ef6865b0f25ddc91e386b99311723b68bd06693c9
|
||||||
size 3664
|
size 3304
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ab6aeb6cba94acf948a0ad64a485db00bf1f3de1360ae4c57212f3f083b2bd24
|
oid sha256:a05a41e66c7a24d461a4bbcdab0979031e5900e1db270af52ca363f0bed521f5
|
||||||
size 2554
|
size 2028
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ed671f4ad1523f0e66498af39e6075a0c19842ae05eddd00871a6e48ed3685d7
|
oid sha256:678483230831d0a7d3dcad5f067a7b641e5d2ae0db477665dfc6c53a675eba18
|
||||||
size 1594
|
size 1779
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5b45645ad9ff27776fdb1caa27827c526cae57f8bd4e23bd1160cb0094121ff2
|
oid sha256:a34885e79f42d19b7777dd07e7ab51df344880cb770c48e0baaddb177c2ae938
|
||||||
size 2338
|
size 2228
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f24039f82d7399d02a155022de65b6dc3b8edcf17059a73a9fd3a9209e3f5575
|
oid sha256:1356fe3ddda14568e9be1dca4e16ca9048852e3a27a3f531cd58d7d368485a82
|
||||||
size 2360
|
size 2362
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:558ea538fb258079f9eb05fe048b2806c7635b9f0452af874b00cb8d79b45f9b
|
oid sha256:50a8ce4fa8ff7f5b0f56ba0dc65b4802dc0be2dc0967b5cb3a15e3b79a4e513e
|
||||||
size 2421
|
size 2424
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4510e65775c6001758ebcf4dc13e9fa561cce5159d1fd54fbb506f22d3c7bdf3
|
oid sha256:61bc44b6e0f99640434d6abcb64880c7bf575eda5cdcf7d74cba7d73307dd39a
|
||||||
size 3149
|
size 2739
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c6137349218ea22adba44f46a096afe2efc35536b2251192ed0ea61be443a3c5
|
oid sha256:f28cdeaba9146521335bc11ad60a8e0368eb0ed1381e88b35a12a6138ba22ed6
|
||||||
size 2493
|
size 2409
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:db20bea98259b204be634ce0d9a23fbfdcfc73a324fc0aac0f9ac54e1c51556d
|
oid sha256:2273629450aa870f0964dd285721c35d3d313fb8b4684122215a65844ae744d0
|
||||||
size 2443
|
size 1888
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user