asic-rs

asic-rs is an async miner management and control library for ASIC miners. It provides one set of concepts across Rust and Python: a factory discovers miners, a miner object gathers data and performs supported control operations, and shared data/config models describe the result.
The Rust crate is published as asic-rs. The Python bindings are published as
pyasic_rs and expose the same high-level API through PyO3 classes and
Pydantic-compatible data models.
API Map
| Concept | Rust | Python |
|---|---|---|
| Discovery and miner construction | MinerFactory |
pyasic_rs.MinerFactory |
| Miner handle | Box<dyn Miner> |
pyasic_rs.Miner |
| Full telemetry snapshot | MinerData |
pyasic_rs.data.MinerData |
| Hashrate values | HashRate, HashRateUnit |
HashRate, HashRateUnit |
| Pool configuration | PoolGroupConfig, PoolConfig |
PoolGroup, Pool |
| Fan configuration | FanConfig |
FanConfig |
| Tuning configuration | TuningConfig |
TuningConfig |
| Optional controls/configs | supports_* methods |
supports_* properties |
All network operations are asynchronous. Rust methods generally return
Result<T> and use Option<T> when a miner does not expose a value. Python
methods are awaitable and return the Python equivalent, using None for missing
or unsupported values.
Examples
The paired examples below use stable markers so documentation tools can render Rust and Python snippets as language tabs while GitHub, PyPI, and docs.rs still show both examples plainly.
Get One Miner
If the miner IP is known, ask MinerFactory to identify the firmware and build
the correct miner implementation.
use MinerFactory;
use ;
async
=
= await
Scan A Network
When the exact IP is not known, add a subnet, octet range, or range string to the factory and scan it. Large scans automatically use bounded concurrency.
use MinerFactory;
async
= await
Other range constructors are available in both languages:
let by_octets = from_octets?;
let by_range = from_range?;
=
=
Stream Scan Results
Use streaming scans when you want to act on miners as soon as they are found instead of waiting for the whole scan to finish.
use MinerFactory;
use StreamExt;
async
=
Gather Data
get_data returns a full MinerData snapshot. Individual get_* calls are
available when only one field is needed.
use MinerFactory;
use ;
async
= await
return
= await
= await
data.operating_state is an optional OperatingState enum for firmware that
reports a detailed runtime state. It distinguishes mining, stable operation,
startup, tuning, frequency/voltage adjustment, idling, pause, suspension,
restriction, stopping, restart, cooldown, degraded mining, and errors.
Mining alone does not promise that tuning is complete: Stable is only used
when the firmware explicitly reports it.
ePIC/UMC, VNish, Braiins REST (25.07+), MARA, and Proto populate this field from
responses already used by the data collector. For example, ePIC’s
AdjustingClockVoltage becomes OperatingState::AdjustingClockVoltage {} in
Rust and OperatingState.AdjustingClockVoltage() in Python. VNish’s
auto-tuning becomes Tuning; Braiins status 3 becomes Paused.
The enum serializes identically in Rust and Python/Pydantic as a tagged object:
{"type": "Mining"} or {"type": "Unknown", "raw": "FutureFirmwareState"}.
Unrecognized labels (and Braiins numeric codes) retain their original value in
Unknown.raw. Rust enums can be matched directly; Python callers can use
isinstance(state, OperatingState.Tuning) or compare against
OperatingState.Tuning(). States are hashable for grouping miners.
Missing, null, invalid, or unsupported state telemetry remains None, including
backends that only expose a boolean, hashrate, or configured work mode. Existing
is_mining behavior is unchanged and may be true during startup or tuning, or
default when a response is missing. It is not derived from operating_state.
Use miner.get_operating_state() to fetch just this field, or exclude
DataField.OperatingState (DataField::OperatingState in Rust) from a snapshot.
To reduce collection work, exclude fields from a full data snapshot.
use DataField;
let data = miner
.get_data_filtered
.await;
= await
Authentication
Backends use their built-in default credentials unless you override them. Set credentials before starting other operations on that miner.
use MinerFactory;
use MinerAuth;
use ;
async
= await
= await
Control A Miner
Control support depends on the miner and firmware. Check the matching
supports_* value before issuing a control command in user-facing tools.
if miner.supports_restart
= await
Configure Pools, Fans, And Tuning
Configuration methods follow the same support pattern as controls. The Python models are Pydantic-compatible, so they can be validated, dumped, and embedded in your own Pydantic models.
use ;
use ;
if miner.supports_pools_config
if miner.supports_fan_config
if miner.supports_tuning_config
=
await
await
await
Python Data Models
Python data/config classes are backed by Rust structs and implement a Pydantic-style surface:
:
=
Use model_validate, model_dump, and model_json_schema on supported model
classes when integrating with Python validation or API layers.