Skip to content

Install

canair isn't on PyPI yet, so install it from a clone of the repository. You need uv (a fast Python package/tool manager).

Install the CLI

git clone https://github.com/philipkocanda/canair.git
cd canair
uv tool install .    # installs the `canair` command globally
canair --help        # first run creates ~/.config/canair/ + a starter config.yaml

The first canair run scaffolds your user config directory and a starter config.yaml. If you run an interactive command that needs a vehicle profile, canair offers a first-run chooser — pick one of the bundled/discovered profiles or create a new one. It tells you exactly where profiles live (~/.config/canair/profiles/) and records your choice as default_profile so later runs are non-interactive. The chooser never fires when piped/scripted, or when you pass --profile/CANAIR_PROFILE. Next: connect your dongle.

Try it without installing

To poke around without a global install, run it straight from the checkout:

uv run canair --help

uv run canair … executes the code in the current repo checkout. This is also what you'll use if you're hacking on canair itself.

Point your writes somewhere durable

uv tool install bakes a frozen copy of canair — including the profiles it ships with — into uv's tool venv, and a reinstall (canair update) replaces that copy wholesale. So a capture or PID edit recorded against a bundled profile from a bare canair is deleted by your next update. Decide once, up front:

# You have the clone (you just used it to install) — keep profile data in it:
canair config set profiles_dir /path/to/canair/profiles

# Or take your own copy of a bundled profile:
canair profile adopt ioniq-2017

A profile you create yourself (canair profile create, or the first-run chooser) already lands in ~/.config/canair/profiles/ and is unaffected. canair warns at the moment of the write if data lands in the snapshot, and canair update lists what a reinstall would delete — but neither is a substitute for the one-time setting above. Details: Profiles → Where your writes land.

Staying up to date

canair checks GitHub once a day (in a background thread — it never blocks a command, and any network failure is silently ignored) for a newer released version. When one is available it prints a one-line notice with a link to the changelog and how to upgrade.

Upgrade with a single command — it checks out the latest release tag in your clone and reinstalls the CLI from it (keeping the git-clone install intact):

canair update            # check, confirm, then checkout <tag> + uv tool install . --reinstall
canair update --check    # report current/latest + changelog only, change nothing
canair update --yes      # skip the confirmation prompt (automation)

Because it checks out the advertised release tag (rather than fast-forwarding main), the installed code is exactly the released version — never unreleased commits sitting on the branch. If the latest release tag can't be determined (GitHub unreachable), canair update reports the offline state and makes no changes rather than guessing a version.

If canair can't find your clone or uv (e.g. a different install method), it prints the exact manual commands instead. To silence the automatic check, set check_for_updates: false in your config (or export CANAIR_NO_UPDATE_CHECK=1).

The two installs can drift out of sync

Running uv tool install . and working in a clone means you have two copies of canair on the machine:

  • a bare canair runs the installed snapshot (uv's tool venv), taken at the last uv tool install;
  • uv run canair runs the repo working tree — whatever you've currently checked out or edited.

Edit the repo (or pull new commits that bump the version) and the two drift: a bare canair keeps reporting the old version while uv run canair reports the new one. canair update detects this — it reports which copy is running and warns when the installed snapshot's version differs from the source clone's pyproject.toml (the same warning also shows up in canair status). When there is no newer release to check out but the two have drifted, canair update offers a reinstall-only resync — it runs uv tool install <clone> --reinstall (no network, no tag checkout) to bring the bare canair back in line with the clone. canair update --json includes the full install block (running_origin, running_build, tool_version, clone_version, out_of_sync) for scripts.

The version tells you which build you're running

An installed release is its version — uv tool install snapshots the clone at a release tag, so 1.15.0 identifies that code exactly. A run from a clone is not any release; it's whatever is checked out. So when canair is running from a git working tree it says so, naming the branch and short commit:

$ canair --version
canair 1.15.0+main.343b244          # a clone, on `main`, at commit 343b244
canair 1.15.0+main.343b244.dirty    # … with uncommitted edits to tracked files
canair 1.15.0                       # an installed release

The same string shows up in canair status, canair update, and — most usefully — in every capture you record, so a suspect reading can be traced back to the exact code that produced it. Release comparisons ignore the suffix, so a dev checkout still gets an accurate "up to date" verdict.

Tab-completion (optional)

Completion covers subcommands, flags, and ECU/PID names from your active profile:

canair completion --install    # auto-detects your shell; open a new shell after

Completion hooks the literal canair command word, so it won't fire through uv run. If you work from a checkout, activate the venv first:

uv sync && source .venv/bin/activate
canair completion --install

Git LFS (for raw-CAN logs)

Large raw-CAN broadcast logs (.blf/.asc/.trc and a profile's captures/can/) are stored with Git LFS. Install it once so a clone fetches the real log contents instead of pointer files:

git lfs install    # one-time, per machine

Without it the tiny bundled fixtures still work (they're plain git), but any committed large log will appear as an LFS pointer. See Broadcast frames → Storing raw-CAN logs for what is committed vs. fetched on demand.