asic-rs 0.8.1

Simple ASIC management in Rust
Documentation
# API Guide

The Rust and Python APIs intentionally share names and behavior. This page
summarizes the user-facing surface and points out the few language-specific
differences.

## Discovery

`MinerFactory` owns the scan range and discovery tuning. Connectivity retries
are additional attempts after the initial probe. Setting retries to zero still
performs one probe; later attempts use bounded exponential backoff and remain
under the same scan concurrency limit. The default remains three retries;
callers that want one pass can explicitly set zero.

Common miner ports are probed concurrently, returning after the first success.
The scan concurrency limit also bounds the total number of active TCP probes,
including probes made by connectivity retries.

The identification timeout is an end-to-end deadline covering discovery
commands and firmware-specific miner construction. Discovery HTTP clients also
apply explicit connection and total-request deadlines.

=== "Rust"

    ```rust
    let factory = MinerFactory::from_subnet("192.168.1.0/24")?
        .with_concurrent_limit(2500)
        .with_connectivity_timeout_secs(1)
        .with_connectivity_retries(0)
        .with_identification_timeout_secs(10);
    ```

=== "Python"

    ```python
    factory = (
        MinerFactory.from_subnet("192.168.1.0/24")
        .with_concurrent_limit(2500)
        .with_connectivity_timeout_secs(1)
        .with_connectivity_retries(0)
        .with_identification_timeout_secs(10)
    )
    ```

| Operation | Rust | Python |
| --- | --- | --- |
| Known IP | `get_miner(ip).await?` | `await get_miner(ip)` |
| Full scan | `scan().await?` | `await scan()` |
| Stream found miners | `scan_stream()` | `scan_stream()` |
| Stream every IP | `scan_stream_with_ip()` | `scan_stream_with_ip()` |

## Miner Identity

Miner identity is available without awaiting because it is known when the miner
handle is constructed.

| Value | Rust | Python |
| --- | --- | --- |
| IP address | `miner.get_ip()` | `miner.ip` |
| Make | `miner.get_device_info().make` | `miner.make` |
| Model | `miner.get_device_info().model` | `miner.model` |
| Firmware | `miner.get_device_info().firmware` | `miner.firmware` |
| Algorithm | `miner.get_device_info().algo` | `miner.algo` |
| Hardware shape | `miner.get_device_info().hardware` | `miner.hardware` |

## Data Collection

The full telemetry snapshot is `MinerData`. Individual getters return focused
fields when a caller does not need the whole snapshot.

=== "Rust"

    ```rust
    let data = miner.get_data().await;
    let hashrate = miner.get_hashrate().await;
    let fans = miner.get_fans().await;
    ```

=== "Python"

    ```python
    data = await miner.get_data()
    hashrate = await miner.get_hashrate()
    fans = await miner.get_fans()
    ```

Common telemetry methods:

| Field | Method |
| --- | --- |
| MAC address | `get_mac` |
| Serial number | `get_serial_number` |
| Hostname | `get_hostname` |
| Firmware version | `get_firmware_version` |
| Hashboards | `get_hashboards` |
| Hashrate | `get_hashrate` |
| Fans | `get_fans` |
| Wattage | `get_wattage` |
| Best share difficulty | `get_best_share` |
| Session best share difficulty | `get_session_best_share` |
| Messages | `get_messages` |
| Pools | `get_pools` |
| Mining state | `get_is_mining` |

## Controls And Capability Checks

Not every miner supports every control. Rust exposes `supports_*()` methods;
Python exposes matching `supports_*` properties.

| Capability | Control |
| --- | --- |
| `supports_restart` | `restart()` |
| `supports_pause` | `pause()` |
| `supports_resume` | `resume()` |
| `supports_set_fault_light` | `set_fault_light(...)` |
| `supports_set_power_limit` | `set_power_limit(...)` |
| `supports_change_password` | `change_password(...)` |
| `supports_read_logs` | `read_logs()` |
| `supports_factory_reset` | `factory_reset()` |
| `supports_prepare_firmware` | `prepare_firmware(...)` |
| `supports_upgrade_firmware` | `upgrade_firmware(...)` |

All miner handles also expose `revalidate()`, which re-runs that backend's
firmware discovery checks against the same IP and returns whether the device is
still valid for the existing miner handle.

`prepare_firmware(...)` is read-only. It validates backend-specific containers
and may query miner metadata to select a compatible payload, but it never starts
an upload. The returned `FirmwareImage` contains the filename and bytes that an
upgrade would use.

=== "Rust"

    ```rust
    if miner.supports_set_power_limit() {
        miner.set_power_limit(measurements::Power::from_watts(3200.0)).await?;
    }
    ```

=== "Python"

    ```python
    if miner.supports_set_power_limit:
        await miner.set_power_limit(3200.0)
    ```

## Configuration Models

Configuration objects are shared concepts across Rust and Python. Python models
are backed by Rust structs and expose Pydantic-style helpers.

=== "Rust"

    ```rust
    use asic_rs::core::config::{
        fan::FanConfig,
        pools::{PoolConfig, PoolGroupConfig},
        tuning::TuningConfig,
    };
    use asic_rs::core::data::{miner::TuningTarget, pool::PoolURL};

    let fan = FanConfig::manual(80);
    let tuning = TuningConfig::new(TuningTarget::from_watts(3200.0));
    let pool = PoolGroupConfig {
        name: "default".to_string(),
        quota: 1,
        pools: vec![PoolConfig {
            url: PoolURL::from("stratum+tcp://pool.example.com:3333".to_string()),
            username: "worker.1".to_string(),
            password: "x".to_string(),
        }],
    };
    ```

=== "Python"

    ```python
    from pyasic_rs.config import FanConfig, Pool, PoolGroup, TuningConfig

    fan = FanConfig.manual(80)
    tuning = TuningConfig.power(3200.0)
    pool = PoolGroup(
        name="default",
        quota=1,
        pools=[
            Pool(
                url="stratum+tcp://pool.example.com:3333",
                username="worker.1",
                password="x",
            )
        ],
    )
    ```

Use the matching config support property before reading or writing config:

| Capability | Get | Set |
| --- | --- | --- |
| `supports_pools_config` | `get_pools_config()` | `set_pools_config(...)` |
| `supports_fan_config` | `get_fan_config()` | `set_fan_config(...)` |
| `supports_tuning_config` | `get_tuning_config()` | `set_tuning_config(...)` |
| `supports_scaling_config` | `get_scaling_config()` | `set_scaling_config(...)` |

## Python Pydantic Interop

Python data/config classes can be used inside Pydantic models and support
`model_validate`, `model_dump`, and `model_json_schema` where applicable.

=== "Python"

    ```python
    from pydantic import BaseModel
    from pyasic_rs.data import HashRate


    class Snapshot(BaseModel):
        hashrate: HashRate


    snapshot = Snapshot.model_validate(
        {"hashrate": {"value": 100.0, "unit": "TH/s", "algo": "SHA256"}}
    )

    print(snapshot.model_dump())
    ```