Crate trackaudio

Crate trackaudio 

Source
Expand description

§TrackAudio client library

trackaudio is a Rust client library for interacting with TrackAudio, a modern voice communication application for VATSIM air traffic controllers.

This crate provides a high-level, async API for controlling TrackAudio programmatically via its WebSocket interface, allowing you to build custom integrations, automation tools, or alternative user interfaces.

§Features

  • Async/await API: Built on Tokio for efficient async I/O
  • Type-safe commands and events: Strongly-typed message protocol
  • Request-response pattern: High-level API for commands that expect responses
  • Event streaming: Subscribe to real-time events from TrackAudio
  • Thread-safe: Client can be safely shared across threads

§Quick start

use trackaudio::{Command, Event, TrackAudioClient};

#[tokio::main]
async fn main() -> trackaudio::Result<()> {
    // Connect to TrackAudio (defaults to ws://127.0.0.1:49080/ws)
    let client = TrackAudioClient::connect_default().await?;

    // Subscribe to events
    let mut events = client.subscribe();

    // Send a command
    client.send(Command::PttPressed).await?;

    // Listen for events
    while let Ok(event) = events.recv().await {
        match event {
            Event::TxBegin(_) => println!("Started transmitting"),
            Event::RxBegin(rx) => println!("Receiving from {}", rx.callsign),
            _ => {}
        }
    }

    Ok(())
}

§High-level API

For common operations you can use TrackAudioApi, which wraps TrackAudioClient and provides typed convenience methods based on the Request pattern:

use std::time::Duration;
use trackaudio::TrackAudioClient;

#[tokio::main]
async fn main() -> trackaudio::Result<()> {
    let client = TrackAudioClient::connect_default().await?;
    let api = client.api();

    // Add a station and wait for its initial state
    let station = api.add_station("LOVV_CTR", Some(Duration::from_secs(5))).await?;
    println!("Added station: {station:?}");

    // Change the main volume
    let volume = api.change_main_volume(-20, None).await?;
    println!("Changed main volume to {volume}");

    Ok(())
}

§Choosing between low-level and high-level APIs

  • Use TrackAudioClient when you want full control:

    • subscribe to the raw Event stream
    • send arbitrary Commands
    • implement your own request/response logic
  • Use TrackAudioApi for common operations with less boilerplate:

    • add/remove stations
    • query station states
    • adjust volumes and other simple settings
    • transmit on all active frequencies

§Architecture

The library is organized around three main parts:

§TrackAudioClient

The core WebSocket client that handles connection management, command sending, and event distribution. Use this for low-level control or when you need to work directly with the command/event protocol.

§TrackAudioApi

A high-level API wrapper that provides convenient methods for common operations with built-in request-response matching and timeout handling.

§Messages

  • Command: Commands you send to TrackAudio (PTT, add station, set volume, etc.)
  • Event: Events emitted by TrackAudio (station updates, RX/TX state changes, etc.)
  • Request: Trait for typed request-response patterns

§Configuration

Use TrackAudioConfig to control connection parameters and internal behavior:

IPv4 and ws:// are currently supported; IPv6 and wss:// are not. Check the documentation of TrackAudioConfig for more details on supported URL variants.

If you’re simply trying to connect to the default TrackAudio instance on your local machine, you can use TrackAudioClient::connect_default to create a client with the default configuration:

use trackaudio::TrackAudioClient;

#[tokio::main]
async fn main() -> trackaudio::Result<()> {
    let client = TrackAudioClient::connect_default().await?;

    Ok(())
}

If you need to customize the connection parameters, you can use TrackAudioClient::connect.

Connecting to a remote TrackAudio instance without modifying additional config values can be performed via TrackAudioClient::connect_url:

use trackaudio::TrackAudioClient;

#[tokio::main]
async fn main() -> trackaudio::Result<()> {
    // See [`TrackAudioConfig`] for supported URL variants.
    let client = TrackAudioClient::connect_url("192.168.1.69").await?;

    Ok(())
}

§Request/response pattern

Some commands support a request/response style interaction by implementing the Request trait. You can send these requests directly via TrackAudioClient::request:

use std::time::Duration;
use trackaudio::TrackAudioClient;
use trackaudio::messages::commands::GetStationStates;

#[tokio::main]
async fn main() -> trackaudio::Result<()> {
    let client = TrackAudioClient::connect_default().await?;

    // Get a snapshot of all station states
    let states = client
        .request(GetStationStates, Some(Duration::from_secs(5)))
        .await?;
    println!("We have {} stations", states.len());

    Ok(())
}

The Request implementation ties a concrete Command (e.g., GetStationStates to the matching Event returned by TrackAudio, and converts them into a typed response (e.g., Vec<StationState>).

§Working with frequencies

TrackAudio returns and expects all frequencies to be in Hertz, however that seem a bit cumbersome to work with. The Frequency type stores values in Hertz internally but offers helpers for Hz, kHz, and MHz:

use trackaudio::Frequency;

let a = Frequency::from_hz(132_600_000);
let b = Frequency::from_khz(132_600);
let c = Frequency::from_mhz(132.600);

assert_eq!(a, b);
assert_eq!(b, c);
assert_eq!(a.as_mhz(), 132.600);

// Convenient conversion to/from MHz as f64
let freq: Frequency = 132.600_f64.into();
let mhz: f64 = freq.into();
assert_eq!(freq.as_mhz(), mhz);

// Convenient conversion to/from Hz as u64
let freq: Frequency = 132_600_000_u64.into();
let hz: u64 = freq.into();
assert_eq!(freq.as_hz(), hz);

§Error Handling

All operations return a Result<T> type with TrackAudioError variants:

use trackaudio::{TrackAudioClient, TrackAudioError};

async fn example() -> Result<(), Box<dyn std::error::Error>> {
    match TrackAudioClient::connect_default().await {
        Ok(client) => { /* use client */ },
        Err(TrackAudioError::WebSocket(e)) => {
            eprintln!("Connection failed: {}", e);
        },
        Err(TrackAudioError::Timeout) => {
            eprintln!("Connection timed out");
        },
        Err(e) => {
            eprintln!("Other error: {}", e);
        }
    }
    Ok(())
}

§Client-emitted events

In addition to events originating from TrackAudio itself, the client can inject internal events via the Event::Client variant. These carry a ClientEvent and are used to report issues such as:

  • Connection loss
  • Failed command transmission
  • Event deserialization errors

You receive them on the same broadcast channel as all other events.

§Features

  • tracing: integrate with the tracing crate and emit spans for key operations (connect, send, request, etc.). Enabled by default.

§External Resources

Re-exports§

pub use messages::ClientEvent;
pub use messages::Command;
pub use messages::Event;
pub use messages::Frequency;

Modules§

messages

Structs§

TrackAudioApi
A high-level client for interacting with the TrackAudio WebSocket API.
TrackAudioClient
TrackAudioClient is a client for interacting with a TrackAudio instance via WebSockets.
TrackAudioConfig
Represents the configuration for a TrackAudioClient.

Enums§

TrackAudioError
The TrackAudioError enum represents various errors that can occur while using this crate.

Type Aliases§

Result
The crate’s Result type, used throughout the library to indicate success or failure.