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.
- π Async/await API β Built on Tokio for efficient async I/O
- π Type-safe β Strongly-typed commands and events with full deserialization
- π 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
- π Tracing support β Optional integration with the
tracingcrate
Add 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(())
}- Use
TrackAudioClientif you want full control (raw events, raw commands). - Use
TrackAudioApiif 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 Hzuse 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 supports flexible URL formats for connecting to TrackAudio:
- Full WebSocket URL:
ws://127.0.0.1:49080/wsorws://192.168.1.69/ws - Host only:
127.0.0.1orlocalhost(uses default port 49080 and/wspath) - Host and port:
127.0.0.1:12345(uses/wspath)
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 conversions
All 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 thetracingcrate for structured logging
To disable tracing:
[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
- Apache License, Version 2.0 (LICENSE-APACHE or https://opensource.org/license/apache-2-0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
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:
- A clear description of the problem
- Steps to reproduce the issue
- The version of this crate used
- Your TrackAudio version
- Your Rust version (
rustc --version) - Your operating system
- Any relevant error messages or logs
- Fork the repository and create a feature branch
- Write tests for new functionality (if applicable)
- Ensure
cargo test,cargo fmt, andcargo clippypass - Update documentation for API changes
- Submit your PR with a clear description of the changes
Note that this project uses Conventional Commits in combination with release-please 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.