Skip to main content

Description

This is a python program for controlling Dante network audio devices (and possibly others in the future). It's early, so expect things to break or switches to change. Use this at your own risk; it's not ready for anything other than a test environment and could make the devices behave unexpectedly. The first goal is to do everything that Dante Controller can do that would be useful for control of the devices from a command-line interface or within scripts.

For more information, check out the gearspace discussion.

Features

Current

  • AVIO input/output gain control
  • Add/remove subscriptions
  • Browser interface served by the daemon
  • CLI
  • Cross-platform foreground daemon plus installable boot service
  • Device lock/unlock through the native Rust protocol core
  • Display active subscriptions, Rx and Tx channels, devices names and addresses, subscription status
  • JSON output
  • Set device latency, sample rate, encoding
  • Set/reset channel names, device names
  • mDNS device discovery

Installation

To install from PyPI:

uv tool install netaudio

Or with pip/pipx:

pip install netaudio

To install from a clone (requires Python 3.9+ and a Rust toolchain, since the native core is compiled from source):

uv sync
uv run netaudio

Arch Linux

To install from AUR, build the package with aur/netaudio.

Usage

Run netaudio if installed globally, or uv run netaudio from a clone.

Subscription status

Subscription JSON keeps the raw 16-bit subscription status.code, separate rx_channel_status.code, and any additional status_message warnings. A connected subscription can carry warnings; it does not prove audio delivery. Managed responses retain ddm_status, ddm_status_message and ddm_summary separately. Their semantic state comes from the shared Rust definitions, while severity follows the managed summary. A null managed status remains unknown, with its available message, summary and channel metadata preserved. GraphQL errors remain full objects in daemon status and leave inventory degraded; unrelated API failures are not treated as successful queries.

Numeric definitions and identifier classification live in netaudio-core. The Python client requires native ABI 6 for these functions. Source checkouts must rebuild the native core after this update. Subscription definitions no longer come from the optional local label file.

The status observation fixture records the evidence scope: a synthetic 0x0000–0x00ff sweep at receiver health 0x0101, observed through one DDM deployment on 2026-09-05. observed_summary is populated for recognized values in the observed receiver contexts. interpretation identifies observed pairs, unverified receiver contexts, unknown numeric values, or code 1 requiring receiver context. Code 1 resolves to DYNAMIC at health 0x0101 and UNRESOLVED at health 0x0000; other contexts remain unresolved. Higher values keep all bits and remain unknown. The sweep does not establish a complete receiver-health precedence rule or naturally occurring hardware faults.

Refreshing discovery

With the daemon running, netaudio discovery refresh requests service discovery again without clearing the current inventory. To query one address directly, use netaudio discovery refresh --address 192.168.1.50. HTTP clients can send POST /discovery/refresh with {} or {"address":"192.168.1.50"}. Replies arrive through the normal discovery and event stream. A successful request does not mean a device answered. Direct mDNS queries do not guarantee discovery across routers. The daemon honors its configured local interface.

Interface selection also binds direct-device control sockets to that interface's IPv4 address. A missing interface or unavailable address is an error; NetAudio does not silently send through another source address. Low-level Python callers can select a source explicitly with CoreClient(..., local_ip="192.0.2.10"). Leaving local_ip unset permits the operating system to select the source. Native callers must use the matching ABI 6 header and library.

Selecting devices and channels

Device commands select devices with the same global filters: -n/--name (glob), -s/--server-name (glob), -m/--mac, and --host (IP address). Commands that act on one device report device not found or multiple devices matched when the filters do not narrow to exactly one. -h is an alias for --help everywhere.

netaudio -n avio-usb-1 device show
netaudio -n avio-usb-1 flow list
netaudio --host 192.168.1.50 lock set 1234

Channels are written as tx:1, rx:1, tx:NAME, rx:NAME, or a bare channel name. A bare name searches both directions and is rejected when it matches both a transmitter and a receiver channel.

netaudio -n avio-usb-1 channel name rx:1
netaudio -n avio-usb-1 channel name rx:1 vocal-in
netaudio -n avio-usb-1 channel gain tx:1 3
netaudio subscription add --tx tx:1@stagebox --rx rx:1@avio-usb-1
netaudio subscription add --tx 1@stagebox --rx 1@avio-usb-1
netaudio subscription remove --rx rx:1@avio-usb-1

With --tx and --rx the direction is implied, so 1@DEVICE is accepted as shorthand for tx:1@DEVICE and rx:1@DEVICE respectively.

Presets are stored in the preset directory (presets/ next to config.toml, or preset_directory in config.toml); preset save NAME, preset show NAME, and preset load NAME use it unless given an explicit .xml path, and preset list shows what is saved there.

The app's Presets page downloads presets as XML and opens XML files for review before applying them. Select devices from the current inventory view; missing entries must be explicitly skipped. Routing and transmitter names are included by default when saving; audio and single-interface network settings are optional. Loading supports receiver subscriptions, transmitter names, sample rate, encoding, latency, preferred leader and network configuration—not a full device backup. Receiver names, AES67 settings and multicast flows are not restored. Applying may interrupt audio or connectivity; the app stops after an unsuccessful or unverified change, reports partial results, and never automatically rolls back or reboots devices.

netaudio -n 'avio-*' preset save stage
netaudio preset list
netaudio config show

Latency configuration and monitoring

Read the complete device-wide latency state for one device:

netaudio -n avio-aes3-1 device config latency

The output distinguishes active, configured, and default values, the device-reported minimum/maximum range, and the latency options produced by filtering the standard option set through that range. JSON, YAML, and XML output include both milliseconds and the original nanosecond values. An active or configured value remains visible when it is inside the reported range but absent from the ordinary option list, or even outside the reported range.

Set latency in milliseconds and require matching active readback:

netaudio -n avio-aes3-1 device config latency 2

netaudio -n DEVICE device show keeps three different layers separate: device-wide configuration, each receiver flow's latency setting and frames per packet, and receiver-flow current/average/peak measurements. Flow settings and live measurements are not treated as aliases of the device-wide value.

Channel and flow status JSON

Channel-status reads follow every continuation page and return one merged record list with page_capacity, page_count, and total_record_count. Channel records carry channel_number, media_type, and media_local_channel_id; flow records carry global_flow_id, media_type, media_local_flow_id, and transmitter_channel_ids_by_slot.

Modern ARC channel-status media type codes are 3 for audio, 4 for video, and 5 for ancillary data. The ancillary label is a causal black-box finding: Dante Controller classified devices publishing code 5 with its ancillary capability filter. The finding establishes Controller's interpretation of the field; the tested devices were synthetic, so it does not establish which physical device families publish ancillary channels.

For the packet-observed frontends, an exact mDNS arcp_vers of 2.8.15 selects protocol 0x280f; the existing 2.8.9 frontend uses 0x2809. Unrecognized or missing versions fail closed instead of selecting a presumed protocol. Enrolled DDM devices use the separately observed 0x2809 managed transport contract.

Dante Domain Manager devices

When the daemon's merged inventory marks a device as enrolled, ordinary device commands automatically use DDM. Unenrolled devices continue to use their local ARC/settings services. A device that has both a local address and enrolled DDM metadata still uses DDM, so commands do not accidentally bypass domain policy.

The guided login discovers DDM servers over mDNS when --url is omitted, authenticates, reads the visible domains, prompts when there is more than one choice, and saves the first context as the default:

netaudio ddm login --username operator
netaudio ddm context list
netaudio ddm context use studio-main

An existing Managed API credential can be used without a password prompt:

netaudio ddm login --url https://ddm.example/graphql --server-profile studio \
  --credential-file ~/.config/netaudio/studio.credential

Each context binds one server profile, one credential file, and one domain ID. The resulting config.toml uses this shape:

[ddm]
default_context = "studio-main"

[ddm.servers.studio]
url = "https://ddm.example/graphql"
credential_file = "credentials/studio.credential"

[ddm.contexts.studio-main]
server = "studio"
domain_id = "0123456789abcdef0123456789abcdef"
domain_name = "Main Studio"

Use netaudio --context CONTEXT ... or NETAUDIO_CONTEXT for a one-command override. The daemon polls every configured server, while each managed device record retains its originating server profile, context, domain ID, and device ID. This keeps devices distinct when separate sites reuse names or IP address ranges and ensures credentials are sent only to the configured server.

The URL is used as configured; there are no separate certificate, hostname, or internal-port settings. Low-level Managed API access is grouped by intent and resource so it does not overwhelm the ordinary DDM commands:

netaudio ddm api read domains
netaudio ddm api write device set-name --device-id DEVICE_ID --name NAME
netaudio ddm api schema

Use the normal device commands for ordinary control. ddm api write exposes schema-derived administrative mutations and should be used deliberately.

The documented GraphQL API supplies inventory and device-name, preferred-leader, and subscription changes. Capture-derived, version-scoped Controller-service support supplies Identify and native ARC/settings requests. The normal CLI paths currently cover device/channel/flow status, device and channel names, latency, sample rate, encoding, gain, AES67, clock state/subdomain, network interface status/configuration, subscriptions, receiver port ranges, and the observed modern flow inventory/delete form. Reads were exercised against enrolled AVIO input and output adapters; mutation paths retain their normal readback and capability checks.

Operations without an established managed request and completion model fail closed instead of trying the unmanaged device address. These currently include lock/unlock, reboot and factory reset, capability/log exports, modern multicast flow creation, channel-name reset, and the 0x2729 transmitter-channel-capability query. Sample-rate pull-up is implemented through the managed settings envelope, but the tested AVIO family did not publish a response and is reported as unavailable.

Run tests:

uv lock --check
uv run --python 3.9 --no-project python -m compileall -q packages/netaudio/src/netaudio
cargo test --manifest-path packages/netaudio-core/Cargo.toml
uv run pytest -q

Lint and format:

uv run ruff check .
uv run ruff format .

Network configuration

netaudio device config interface reads active and configured settings for both primary and secondary interfaces. Primary settings can be changed with dhcp or static --ip ADDRESS --netmask MASK [--gateway ADDRESS] [--dns ADDRESS]. Secondary settings are currently read-only; secondary writes are rejected.

netaudio device config redundancy reads Dante Redundancy. Add switched, redundant, or split_redundant to request a supported mode. Support is scoped to the observed A32 Switched/Redundant and AD4D Switched/Split/Redundant variants; unrecognized modes and network protocols are unavailable.

The device's Network Config panel provides the same controls and distinguishes active values from configured values awaiting reboot. Changes use fresh preflight and configured-value readback. An uncertain result is reported without automatically retrying or rebooting. Network changes can interrupt connectivity; reboot the device separately when ready.

Browser interface

The daemon serves a browser interface from its own HTTP port, so no extra process or build step is involved. Start the daemon and open the address it reports:

netaudio daemon start
netaudio daemon web --open

The page is a single-page application backed entirely by the daemon HTTP API and its /events stream, so device, subscription, metering, and Shure state update live without polling. It uses real browser history routing, so every view, device, and device tab is a linkable address. It follows Dante Controller's layout and vocabulary so it is immediately familiar:

  • Routing: the full network subscription matrix, Dante receivers across and Dante transmitters down by default, with devices of up to 16 channels expanded initially. Click a device intersection to expand or collapse its channels, click a channel intersection to change its subscription, and filter either axis by device or channel name. The matrix is canvas-rendered and stays responsive across tens of thousands of cells; pending changes, subscribed, warning, and error states are drawn as in Dante Controller.
  • Device Info, Clock Status, Network Status: the network-view tables with the columns Dante Controller operators expect. Every table lets columns be hidden, shown, and dragged into a new order, remembered per table in the browser.
  • Device view: Receive, Transmit, Metering, Status, Device config, Network config, AES67 config, Transmit flows, Device lock, and Domain tabs. Receivers subscribe through a searchable picker; channels can be renamed and gains set where the device supports it.
  • Metering: live meters for every declared channel, drawn on canvas with peak hold. Both metering protocols are surfaced: detailed per-channel levels streamed after an explicit start, and the passive signal-presence records a device already broadcasts.
  • Flows: transmit flow inventory per device, with multicast flow creation and deletion.
  • Domains: Dante Domain Manager status, domains, and the managed inventory.
  • Shure: discovered receivers, channel state, transmitter and battery detail, and live meter values.
  • Events: the daemon event stream, held in memory only and excluding meter samples.

Use Search to find a device or view. On phones, Routing presents receiving channels as touch-sized controls with source selection in a bounded dialog. Larger screens offer the crosspoint grid or channel list; grid headers show subscription indicators even when a connection is off-screen. Network views are directly accessible through the workspace tabs; the Tools menu provides subscriptions, presets, domains, Shure, and settings without occupying the workspace. Phones use a single view selector containing every page. Each inventory view starts with focused columns; all additional fields remain available through Columns. Device pages provide a device switcher and consistent section navigation. See interface principles for the app-wide model.

Offline devices are excluded from browser inventory lists and search. Device details, tables, notices, and error text can always be selected and copied.

The client uses Preact, Tailwind CSS, daisyUI components, and Lucide icons. Assets are bundled: end users need neither Node nor an external CDN. For UI development, run npm ci and npm run build:webapp after editing components or styles. Edit CSS sources in scripts/webapp/, not the generated app.css. Run npm run test:webapp for model/render tests and npx playwright test for fixture-backed phone and desktop interactions (install test browsers with npx playwright install chromium webkit).

Device mutations use the same verified-write paths as the CLI, so a control that reports success has been read back from the device. The daemon binds every interface; restrict access at the network layer if that is not wanted.

Documentation

Metadata

Release files for netaudio 0.3.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for netaudio 0.3.3
File Size Uploaded
netaudio-0.3.3.tar.gz 690.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for netaudio 0.3.3
File
netaudio-0.3.3-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
netaudio-0.3.3-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
netaudio-0.3.3-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
netaudio-0.3.3-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
netaudio-0.3.3-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 7.0 MB

Release files / netaudio-0.3.3.tar.gz

Download URL netaudio-0.3.3.tar.gz
Size 690.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7d348fc5e3df7aeed66aa6b42a265149f1e879c60e84cc85736dc7d7bc4ffcbe
BLAKE2b-256 checksum
How to use checksums
3f3eecfe31fbd736df0ae0ca8423b7808ba80e43484d6f2ad4419726d33193f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / netaudio-0.3.3-py3-none-win_amd64.whl

Download URL netaudio-0.3.3-py3-none-win_amd64.whl
Size 1.2 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
095e2d4e293e16acb82ba8f960b238e9325fc656476c41612ce13f534d892e91
BLAKE2b-256 checksum
How to use checksums
c7b67d7462df7838d87ae92cce83be58abda4c3508eb5376247fabeaf11300dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / netaudio-0.3.3-py3-none-manylinux_2_28_x86_64.whl

Download URL netaudio-0.3.3-py3-none-manylinux_2_28_x86_64.whl
Size 1.3 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
c7443651c642a7225268fbb895466c6dfd35440b1ebabbf4ffd60de7cf98b7ba
BLAKE2b-256 checksum
How to use checksums
e619790219921ec74384ad6b9562b18aad947fab4d417ec2744a7e9dae6bfcce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / netaudio-0.3.3-py3-none-manylinux_2_28_aarch64.whl

Download URL netaudio-0.3.3-py3-none-manylinux_2_28_aarch64.whl
Size 1.3 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
8b1da6298ca0ef313e014e2e8b502c5b1f0e499d71046ce782b7e01ace148049
BLAKE2b-256 checksum
How to use checksums
bc372067a71142b069cb0136e30cdfb94ea22f0580d27e8abc37d2a173a534a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / netaudio-0.3.3-py3-none-macosx_11_0_x86_64.whl

Download URL netaudio-0.3.3-py3-none-macosx_11_0_x86_64.whl
Size 1.3 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
cc1c1c718919c704f089c2381dcca7ac19c778f488795537c465f5bbe259eb71
BLAKE2b-256 checksum
How to use checksums
f699c8cdb2b55fbe4bfa65dd3567724442402d024c1c5f688f7ff3e74ff3c46b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / netaudio-0.3.3-py3-none-macosx_11_0_arm64.whl

Download URL netaudio-0.3.3-py3-none-macosx_11_0_arm64.whl
Size 1.3 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
79b46555801c37dbf6159d65856a9adb4aaac524e525794d2c6ed0f4b26ca978
BLAKE2b-256 checksum
How to use checksums
e443962237a6ed268f4ef7a2f14a42eb5c8f4cc2c531799df98128a6fa8ab422
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.14

6 release files

0.3.13

6 release files

0.3.12

6 release files

0.3.9

6 release files

0.3.7

6 release files

0.3.6

6 release files

0.3.5

6 release files

0.3.4

6 release files

This release

0.3.3 This release

6 release files

0.3.1

6 release files

0.3.0

6 release files

0.2.5

5 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release 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