Skip to main content

xair-cli

Control Behringer X-Air and Midas MR mixers from the command line.

I wanted my mixer scriptable, so this talks OSC to the console directly instead of driving the X AIR Edit GUI. Faders, mutes, sends, scenes, and soundcheck routines all become shell commands you can commit to a repo.

$ xair discover
192.168.1.50     XR18   "Main Room"

$ xair --ip 192.168.1.50 --json health
{"status": "ok", "ip": "192.168.1.50", "name": "Main Room", "model": "XR18", "version": "1.18", "connect": "ok", "info": "ok"}

Features

  • Faders, mutes, pans, EQ, dynamics, preamp gain, and sends
  • Multi-channel targets: ranges (1-4), lists (1,3,5), or all
  • Scene load, save, and list
  • Mixer discovery over UDP broadcast
  • Batch scripts from a file or stdin
  • Three output modes: human-readable, JSON, and key=value
  • Shell completion for bash, zsh, and fish

Supported mixers

  • Behringer XR18, XR16, XR12
  • Midas MR18
  • Behringer X32

Installation

From source (repo root). This is the only path that gives you the build this README describes:

./setup.sh           # creates ./.venv if needed, installs the package into it
./setup.sh --dev     # same, plus the test/lint/build extras
source .venv/bin/activate

setup.sh installs into an already-activated virtualenv if there is one, otherwise into ./.venv. It uses uv when available and pip when not, and prints which environment and installer it used. Set PYTHON=/path/to/python3.x to choose the interpreter for the virtualenv it creates.

Or manually:

python -m venv .venv
source .venv/bin/activate
pip install -e "./xair-cli"

Why not pip install dc-xair-cli right now. The package exists on PyPI, but the published wheel labeled 2.4.3 was built from 2.4.1-era code: it predates the negative-value parsing, the misplaced-flag guards, and the --dry-run safety work this README documents. Worst of the differences: xair --dry-run discover from that wheel puts a real UDP broadcast on the wire, which this README promises never happens. It also omits the docs command and ships without LICENSE and NOTICE. You can tell the builds apart: the stale wheel prints 2.4.1 from xair --version; a source build prints the current version. PyPI filenames are immutable, so the fix will arrive as a release newer than 2.4.3; until one appears there, install from source. I will remove this caveat when that release lands.

Quick start

# Discover mixers on the network
xair discover

# Show mixer info
xair --ip 192.168.1.50 info

# Control channels
xair --ip 192.168.1.50 ch 1 fader -6
xair --ip 192.168.1.50 ch 1-4 mute on

# Load a scene
xair --ip 192.168.1.50 scene load 1

See QUICKSTART.md for more examples.

Configuration

Create a config file to avoid passing --ip every time:

xair config init

Edit ~/.config/xair/config.toml:

[connection]
ip = "192.168.1.50"
model = "XR18"

Or set environment variables:

export XAIR_IP="192.168.1.50"
export XAIR_MODEL="XR18"

Commands

Command Description
info Show mixer model, IP, and firmware
discover Find mixers on the network
health Run a read-only check of the connection
status Show channel overview
ch Control input channels
bus Control output buses
lr Control main L/R output
dca Control DCA groups
fx Control FX slots
rtn Control FX returns
scene Manage scenes (load/save/list)
raw Send raw OSC commands
meters Show channel levels
batch Execute commands from file
config Manage configuration
completion Generate shell completions
docs Create or update documentation templates

xair health exits non-zero when the mixer cannot be reached, so it works as a readiness check in scripts:

xair --json health --details          # timings for connect and info
xair health --auto-discover           # find the mixer first, then check it

Channel properties

Property Description Range
fader Level in dB -90 to +10
mute Mute state on/off/toggle
name Channel name string
gain Preamp gain (channels only) 0-60 dB
phantom 48V phantom power on/off
pan Pan position -100 to +100
eq EQ enable on/off
gate Gate enable on/off
comp Compressor enable on/off
send.N Send level to bus N -90 to +10 dB

Not every channel type has every property. DCA groups expose mute, name, and color only; use xair raw for anything a typed command does not cover.

Negative and relative values

A leading dash is a value, not an option. Absolute negatives work verbatim:

xair ch 1 fader -6      # Set fader to -6 dB
xair ch 1 pan -50       # Pan halfway left
xair ch 1 send.1 -10    # Set send to bus 1 to -10 dB

Use + or -- for changes relative to the current value:

xair ch 1 fader +3      # 3 dB louder than it is now
xair ch 1 fader --6     # 6 dB quieter than it is now (double dash)
xair bus 1-4 fader --3  # 3 dB quieter on every bus in the range

So -6 means "set to -6 dB" and --6 means "turn down by 6 dB".

Dry run

--dry-run prints the OSC address and value each command would send and exits without contacting a mixer. It needs no IP and no network:

$ xair --dry-run ch 1 fader -6
Dry-run mode: nothing is sent to XR18 (no IP configured)
[dry-run] would query /xinfo
[dry-run] would query /ch/01/mix/fader
[dry-run] would send  /ch/01/mix/fader 0.6
fader = -6.0 dB

Commands that would otherwise reach the network are previewed too, so --dry-run never puts a packet on the wire:

$ xair --dry-run discover
DRY RUN: would broadcast discovery on port 10024

Preview lines go to stderr, so stdout carries only the command's own output: under --json that is one JSON document per affected target, in dry-run and live alike.

$ xair --json --dry-run ch 1-2 fader -6 2>/dev/null
{"fader": -6.0, "unit": "dB"}
{"fader": -6.0, "unit": "dB"}

Values read back in dry-run are placeholders; there is no mixer to ask.

Output formats

xair ch 1 fader              # Human readable
xair --json ch 1 fader       # JSON output
xair --plain ch 1 fader      # key=value pairs

Shell completion

# Bash
xair completion bash >> ~/.bashrc

# Zsh
xair completion zsh >> ~/.zshrc

# Fish
xair completion fish > ~/.config/fish/completions/xair.fish

Repository layout

The Python package lives one level down, in xair-cli/, so the repository root holds the workspace and the package directory holds the distribution:

.                       repository root (docs, setup.sh, CI workflows)
└── xair-cli/           the dc-xair-cli package root
    ├── pyproject.toml  build config, entry point, pytest settings
    ├── src/xair/       CLI layer (Click commands, context, output)
    ├── lib/xair_api/   vendored OSC mixer API (see NOTICE)
    └── tests/          pytest suite

Two consequences worth knowing before you clone:

  • Test commands run from xair-cli/, not the repository root. ./setup.sh installs from there for you and can be run from anywhere.
  • README.md, LICENSE, and NOTICE exist at the root and again in xair-cli/, kept byte-identical. The build backend only sees the inner copies and puts them in the published package, while GitHub renders the outer ones. Edit both.

Development

cd xair-cli
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

pytest                       # test suite
ruff check . && ruff format . --check
mypy .

CI runs the same four commands plus python -m build on every pull request.

Documentation and policies

  • CONTRIBUTING.md
    • development setup, the checks CI runs, and how to open a pull request.
  • SECURITY.md
    • how to report a vulnerability privately, and what is in scope.
  • QUICKSTART.md
    • a longer worked example than the section above.
  • xair-cli/CHANGELOG.md
    • the release history.
  • docs/
    • docs/archive/ holds superseded planning notes, kept for reference and not maintained.

Non-goals

  • No GUI. This is a command-line tool, and xair raw is the escape hatch for anything the typed commands do not cover.
  • No mixer emulator. The test suite uses a mock mixer plus the --dry-run transport, so it proves argument parsing, dispatch, OSC address construction, and output formatting. It does not prove behavior against real hardware.
  • Shell completions are static. They do not reflect the connected mixer's state.
  • Discovery relies on UDP broadcast, so it will not cross subnets.

Third-party code

xair-cli/lib/xair_api/ is a vendored and modified copy of xair-api by Onyx and Iris, used under the MIT License. See NOTICE for attribution and the copyright lines it preserves.

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dc_xair_cli-2.4.4.tar.gz (64.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dc_xair_cli-2.4.4-py3-none-any.whl (60.9 kB view details)

Uploaded Python 3

File details

Details for the file dc_xair_cli-2.4.4.tar.gz.

File metadata

  • Download URL: dc_xair_cli-2.4.4.tar.gz
  • Upload date:
  • Size: 64.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for dc_xair_cli-2.4.4.tar.gz
Algorithm Hash digest
SHA256 30a2c773c594449f32a273cf6e885b3e6c2614deaee2f5c34782880a7cf1a07f
MD5 afb1a90392d7c834aa1c96842d4c3925
BLAKE2b-256 98dd02866d7820d930f390eed66ed22ddef7419b361a7bd2a0aa8e1af250cac4

See more details on using hashes here.

File details

Details for the file dc_xair_cli-2.4.4-py3-none-any.whl.

File metadata

  • Download URL: dc_xair_cli-2.4.4-py3-none-any.whl
  • Upload date:
  • Size: 60.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for dc_xair_cli-2.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 c09b60183319edc08e3bf426e695c3682631aa384e83f91bb5f48e869a2c8947
MD5 d9856e8e386bb791b38e2939a39b5787
BLAKE2b-256 a5d3fe588b78dfd25976c157626237b748daf02216f3fd969f3c9d7f1fd08db3

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.4.4 This release

2 files

2.4.3

2 files

2.4.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page