| Crates.io | trackaudio |
| lib.rs | trackaudio |
| version | 0.2.1 |
| created_at | 2025-11-20 13:57:49.170778+00 |
| updated_at | 2025-12-06 20:00:31.838541+00 |
| description | A high-level async client for the TrackAudio WebSocket API, enabling programmatic control, automation, and custom integrations for VATSIM voice communication. |
| homepage | |
| repository | https://github.com/MorpheusXAUT/trackaudio-rs |
| max_upload_size | |
| id | 1942040 |
| size | 186,163 |
A Rust client library for 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, enabling custom integrations, automation tools, and alternative user interfaces.
tracing crateAdd the dependency to your Cargo.toml:
[dependencies]
trackaudio = "0.1"
Connect to the default instance locally and listen for events:
use trackaudio::{Command, Event, TrackAudioClient};
#[tokio::main]
async fn main() -> trackaudio::Result<()> {
// Connect to the local TrackAudio instance (ws://127.0.0.1:49080/ws)
let client = TrackAudioClient::connect_default().await?;
// Subscribe to event stream
let mut events = client.subscribe();
// Send a command
client.send(Command::PttPressed).await?;
// Handle incoming events
while let Ok(event) = events.recv().await {
match event {
Event::TxBegin(_) => println!("Started transmitting"),
Event::RxBegin(rx) => println!("Receiving from {}", rx.callsign),
_ => {}
}
}
Ok(())
}
For common operations, use the TrackAudioApi wrapper:
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 TrackAudio's response
let station = api.add_station("LOVV_CTR", Some(Duration::from_secs(5))).await?;
println!("Added station: {station:?}");
// Change main volume
let vol = api.change_main_volume(-20, None).await?;
println!("Volume changed to {vol}");
Ok(())
}
TrackAudioClient if you want full control (raw events, raw commands).TrackAudioApi if you want convenience with timeout-guarded request/response helpers.For more detailed examples and API docs, check out the documentation.
use trackaudio::{Command, Frequency, TrackAudioClient};
use trackaudio::messages::commands::{BoolOrToggle, SetStationState};
use std::time::Duration;
#[tokio::main]
async fn main() -> trackaudio::Result<()> {
let client = TrackAudioClient::connect_default().await?;
let api = client.api();
// Add a station
let station = api.add_station("LOVV_CTR", Some(Duration::from_secs(5))).await?;
if station.is_available {
println!("β Station available on {}", station.frequency.unwrap());
// Enable RX and TX on the station
client.send(Command::SetStationState(SetStationState {
frequency: station.frequency.unwrap(),
rx: Some(BoolOrToggle::Bool(true)),
tx: Some(BoolOrToggle::Bool(true)),
xca: None,
is_output_muted: None,
headset: None,
})).await?;
} else {
println!("β Station not available");
}
Ok(())
}
use trackaudio::Frequency;
// Create frequencies in different units
let freq_hz = Frequency::from_hz(132_600_000);
let freq_khz = Frequency::from_khz(132_600);
let freq_mhz = Frequency::from_mhz(132.600);
assert_eq!(freq_hz, freq_khz);
assert_eq!(freq_khz, freq_mhz);
// Convert between units
println!("Frequency: {} MHz", freq_hz.as_mhz());
println!("Frequency: {} kHz", freq_hz.as_khz());
println!("Frequency: {} Hz", freq_hz.as_hz());
// Convenient conversions
let freq: Frequency = 132.600_f64.into(); // from MHz
let freq: Frequency = 132_600_000_u64.into(); // from Hz
use trackaudio::{Event, TrackAudioClient};
#[tokio::main]
async fn main() -> trackaudio::Result<()> {
let client = TrackAudioClient::connect_default().await?;
let mut events = client.subscribe();
println!("Monitoring radio activity...");
while let Ok(event) = events.recv().await {
match event {
Event::RxBegin(rx) => {
println!("π» RX Start: {} on {}", rx.callsign, rx.frequency);
}
Event::RxEnd(rx) => {
println!("π» RX End: {} on {}", rx.callsign, rx.frequency);
if let Some(active) = rx.active_transmitters {
if !active.is_empty() {
println!(" Still transmitting: {}", active.join(", "));
}
}
}
Event::TxBegin(_) => {
println!("ποΈ TX Start");
}
Event::TxEnd(_) => {
println!("ποΈ TX End");
}
Event::StationStateUpdate(state) => {
println!("π‘ Station update: {}", state.callsign);
}
_ => {}
}
}
Ok(())
}
use trackaudio::TrackAudioClient;
use trackaudio::messages::commands::{GetStationState, GetStationStates};
use std::time::Duration;
#[tokio::main]
async fn main() -> trackaudio::Result<()> {
let client = TrackAudioClient::connect_default().await?;
// Request a specific station's state
let station = client
.request(
GetStationState {
callsign: "LOVV_CTR".to_string(),
},
Some(Duration::from_secs(5)),
)
.await?;
println!("Station: {:?}", station);
// Request all station states
let states = client
.request(GetStationStates, Some(Duration::from_secs(5)))
.await?;
println!("Total stations: {}", states.len());
Ok(())
}
use trackaudio::{TrackAudioClient, TrackAudioConfig};
use std::time::Duration;
#[tokio::main]
async fn main() -> trackaudio::Result<()> {
let config = TrackAudioConfig::new("192.168.1.69:49080")?
.with_capacity(512, 512) // Increase channel capacities
.with_ping_interval(Duration::from_secs(10)); // Adjust keepalive
let client = TrackAudioClient::connect(config).await?;
// Use client...
Ok(())
}
The client automatically reconnects on connection loss with exponential backoff:
use trackaudio::{TrackAudioClient, TrackAudioConfig, ConnectionState, ClientEvent, Event};
use std::time::Duration;
#[tokio::main]
async fn main() -> trackaudio::Result<()> {
let config = TrackAudioConfig::default()
.with_auto_reconnect(true) // Enabled by default
.with_max_reconnect_attempts(Some(10)) // None = infinite retries
.with_backoff_config(
Duration::from_secs(1), // Initial backoff
Duration::from_secs(60), // Max backoff
2.0, // Multiplier
);
let client = TrackAudioClient::connect(config).await?;
// Monitor connection state changes
let mut events = client.subscribe();
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
if let Event::Client(ClientEvent::ConnectionStateChanged(state)) = event {
match state {
ConnectionState::Disconnected { reason } => {
println!("Disconnected: {reason}");
}
ConnectionState::Reconnecting { attempt, next_delay } => {
println!("Reconnecting (attempt {attempt}) in {next_delay:?}...");
}
ConnectionState::Connected => {
println!("Connected!");
}
_ => {}
}
}
}
});
// Manually trigger reconnection if needed
client.reconnect().await?;
Ok(())
}
The client supports flexible URL formats for connecting to TrackAudio:
ws://127.0.0.1:49080/ws or ws://192.168.1.69/ws127.0.0.1 or localhost (uses default port 49080 and /ws path)127.0.0.1:12345 (uses /ws path)Note: TrackAudio currently only supports IPv4 connections and the ws:// scheme (no TLS/wss:// support).
TrackAudioClient β The core WebSocket client for connection managementTrackAudioApi β High-level API wrapper with convenient methodsTrackAudioConfig β Configuration for connection parametersCommand β Commands to send to TrackAudioEvent β Events emitted by TrackAudioFrequency β Radio frequency with convenient unit conversionsAll operations return a Result<T, TrackAudioError>:
use trackaudio::{TrackAudioClient, TrackAudioError};
match TrackAudioClient::connect_default().await {
Ok(client) => {
// Use client
}
Err(TrackAudioError::WebSocket(e)) => {
eprintln!("Connection failed: {}", e);
}
Err(TrackAudioError::Timeout) => {
eprintln!("Operation timed out");
}
Err(TrackAudioError::InvalidUrl(msg)) => {
eprintln!("Invalid URL: {}", msg);
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
tracing (enabled by default) β Integrate with the tracing crate for structured loggingreconnect-jitter (enabled by default) β Add jitter to reconnection backoff to prevent thundering herdTo disable default features:
[dependencies]
trackaudio = { version = "0.1", default-features = false }
This crate requires Rust 1.85 or later.
The trackaudio project and all its crates and packages are dual-licensed as
at your option.
This means you can choose to use trackaudio under either the Apache-2.0 license or the MIT license.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Contributions are welcome! Whether you've found a bug, have a feature request, or want to improve the documentation, your input is valued.
If you encounter a bug or have a feature request, please open an issue on GitHub. When reporting a bug, please include:
rustc --version)cargo test, cargo fmt, and cargo clippy passNote that this project uses Conventional Commits in combination with release-plz for automatic semantic versioning and release automation.
Please ensure your commits follow this format when submitting a PR.
For significant changes, please open an issue first to discuss your proposal.
This is an unofficial client library. TrackAudio is developed and maintained by Pierre.