Three kinds of call
Fire-and-forget, blocking query, no round-tripEvery Device method is one of three kinds, tagged with a badge on the API pages.
Fire-and-forget
Writes one frame, returns once the bytes are out, no reply.
device.move_rel(100, -50)?; // one frame out, no replyBlocks
Sends a QUERY and waits for the correlated RESP.
let v = device.query_version()?; // waits for the box to replyNo round-trip
Reads state the library already holds; can't fail on the link.
let c = device.counters(); // local snapshot, no networkWhy the queries are async
Queries await a reply, everything else fires and forgetsWith the async feature, the QUERY methods are the async fns, because a query blocks for its correlated RESP. Every other method is fire-and-forget, so it stays synchronous.
Async (you .await it) | Stays sync (no .await) |
|---|---|
query_version, query_health, device_info, caps, query_rate, query_stats, query_locks, query_catch, the option query_* methods, and the clip status query | move_rel, wheel, inject, press, release, force_release, reset, reboot, led, lock, unlock, catch_events, and the clip append / start / stop |
Driving futures without a runtime
futures::executor::block_onblock_on runs one future to completion on the current thread, so you can await a query with no async runtime at all.
use futures::executor::block_on;
let device = Device::find()?.into_async();
let v = block_on(device.query_version())?;
println!("{v}");When the box is silent
Default timeout and QueryTimeoutA query waits DEFAULT_QUERY_TIMEOUT (1 second), then returns Err(Error::QueryTimeout). This applies to both the sync and async queries.
match device.query_health() {
Ok(h) => println!("{h:?}"),
Err(medius::Error::QueryTimeout) => eprintln!("no reply in time"),
Err(e) => return Err(e),
}QueryTimeout is a query hitting its deadline; NoReply is the handshake's own silence. Both are on the Errors page.
Smooth motion
Subdivide the delta, pace the stepsmove_rel applies one delta at once. To spread it over time, subdivide the move and pace the steps yourself, roughly one per millisecond. There's no move_smooth.
use std::thread::sleep;
use std::time::Duration;
// Glide ~400 counts to the right over 200 steps (~200 ms at 1 kHz).
for _ in 0..200 {
device.move_rel(2, 0)?;
sleep(Duration::from_millis(1));
}The library applies no rate limit. A no-sleep loop queues frames faster than 4 Mbaud drains; pace your own steps.
Making a click
Press, wait, releaseThere's no one-shot click: press, wait, then release with release which clears only the box's own override.
use std::{thread, time::Duration};
use medius::Button;
device.press(Button::Left)?;
thread::sleep(Duration::from_millis(20));
device.release(Button::Left)?;reset drops every override at once; a held press is re-asserted on reconnect via reapply.