docs: use different docs theme, update docs and examples, and improve docs build
This commit is contained in:
189
docs/index.md
189
docs/index.md
@@ -11,145 +11,149 @@
|
||||
[](https://pyasic.readthedocs.io/en/latest/)
|
||||
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
||||
|
||||
---
|
||||
## Intro
|
||||
---
|
||||
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
||||
|
||||
[Click here to view supported miner types](miners/supported_types.md)
|
||||
|
||||
---
|
||||
## Installation
|
||||
|
||||
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include:
|
||||
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default
|
||||
|
||||
```
|
||||
poetry install
|
||||
```
|
||||
|
||||
- [venv](https://docs.python.org/3/library/venv.html): included in Python standard library but has fewer features than other options
|
||||
- [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv): [pyenv](https://github.com/pyenv/pyenv) plugin for managing virtualenvs
|
||||
|
||||
```
|
||||
pyenv install <python version number>
|
||||
pyenv virtualenv <python version number> <env name>
|
||||
pyenv activate <env name>
|
||||
```
|
||||
|
||||
- [conda](https://docs.conda.io/en/latest/)
|
||||
|
||||
##### Installing `pyasic`
|
||||
|
||||
`python -m pip install pyasic` or `poetry install`
|
||||
|
||||
---
|
||||
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system.
|
||||
`pyasic` can be installed directly from pip, either with `pip install pyasic`, or a different command if using a tool like `pypoetry`.
|
||||
|
||||
## Getting started
|
||||
---
|
||||
Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
|
||||
|
||||
##### Scanning for miners
|
||||
### Scanning for miners
|
||||
To scan for miners in `pyasic`, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
|
||||
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
|
||||
```python
|
||||
import asyncio # asyncio for handling the async part
|
||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
||||
```python3
|
||||
import asyncio# (1)!
|
||||
from pyasic.network import MinerNetwork# (2)!
|
||||
|
||||
|
||||
async def scan_miners(): # define async scan function to allow awaiting
|
||||
# create a miner network
|
||||
# you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
||||
network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
|
||||
async def scan_miners():# (3)!
|
||||
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
||||
|
||||
# scan for miners asynchronously
|
||||
# this will return the correct type of miners if they are supported with all functionality.
|
||||
miners = await network.scan()
|
||||
miners = await network.scan()# (5)!
|
||||
print(miners)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(scan_miners()) # run the scan asynchronously with asyncio.run()
|
||||
asyncio.run(scan_miners())# (6)!
|
||||
```
|
||||
|
||||
1. `asyncio` for handling the async part.
|
||||
2. `MinerNetwork` handles the scanning.
|
||||
3. Define an async function to allow awaiting.
|
||||
4. Create a miner network.
|
||||
You can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
||||
This uses the 192.168.1.0-255 network.
|
||||
5. Scan for miners asynchronously.
|
||||
This will return the correct type of miners (if they are supported) with all functionality.
|
||||
6. Run the scan asynchronously with asyncio.run().
|
||||
|
||||
---
|
||||
##### Creating miners based on IP
|
||||
### Creating miners based on IP
|
||||
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner].
|
||||
The function [`get_miner()`][pyasic.miners.get_miner] will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner.
|
||||
```python
|
||||
import asyncio # asyncio for handling the async part
|
||||
from pyasic import get_miner # handles miner creation
|
||||
import asyncio# (1)!
|
||||
from pyasic import get_miner# (2)!
|
||||
|
||||
|
||||
async def get_miners(): # define async scan function to allow awaiting
|
||||
# get the miner with the miner factory
|
||||
# the miner factory is a singleton, and will always use the same object and cache
|
||||
# this means you can always call it as MinerFactory().get_miner(), or just get_miner()
|
||||
miner_1 = await get_miner("192.168.1.75")
|
||||
async def get_miners():# (3)!
|
||||
miner_1 = await get_miner("192.168.1.75")# (4)!
|
||||
miner_2 = await get_miner("192.168.1.76")
|
||||
print(miner_1, miner_2)
|
||||
|
||||
# can also gather these, since they are async
|
||||
# gathering them will get them both at the same time
|
||||
# this makes it much faster to get a lot of miners at a time
|
||||
tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")]
|
||||
miners = await asyncio.gather(*tasks)
|
||||
miners = await asyncio.gather(*tasks)# (5)!
|
||||
print(miners)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run()
|
||||
asyncio.run(get_miners())# (6)!
|
||||
```
|
||||
|
||||
---
|
||||
1. `asyncio` for handling the async part.
|
||||
2. `get_miner` handles the miner type selection.
|
||||
3. Define an async function to allow awaiting.
|
||||
4. Get the miner.
|
||||
5. Can also gather these, since they are async.
|
||||
Gathering them will get them both at the same time.
|
||||
This makes it much faster to get a lot of miners at a time.
|
||||
6. Get the miners asynchronously with asyncio.run().
|
||||
|
||||
## Data gathering
|
||||
---
|
||||
Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`.
|
||||
This function will return an instance of the dataclass [`MinerData`][pyasic.data.MinerData] with all data it can gather from the miner.
|
||||
Each piece of data in a [`MinerData`][pyasic.data.MinerData] instance can be referenced by getting it as an attribute, such as [`MinerData().hashrate`][pyasic.data.MinerData].
|
||||
|
||||
##### One miner
|
||||
### One miner
|
||||
```python
|
||||
import asyncio
|
||||
from pyasic import get_miner
|
||||
import asyncio# (1)!
|
||||
from pyasic import get_miner# (2)!
|
||||
|
||||
async def gather_miner_data():
|
||||
miner = await get_miner("192.168.1.75")
|
||||
if miner is not None:
|
||||
miner_data = await miner.get_data()
|
||||
print(miner_data) # all data from the dataclass
|
||||
|
||||
async def gather_miner_data():# (3)!
|
||||
miner = await get_miner("192.168.1.75")# (4)!
|
||||
if miner is not None:# (5)!
|
||||
miner_data = await miner.get_data()# (6)!
|
||||
print(miner_data)# (7)!
|
||||
print(miner_data.hashrate) # hashrate of the miner in TH/s
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(gather_miner_data())
|
||||
asyncio.run(gather_miner_data())# (9)!
|
||||
```
|
||||
---
|
||||
##### Multiple miners
|
||||
|
||||
1. `asyncio` for handling the async part.
|
||||
2. `get_miner` handles the miner type selection.
|
||||
3. Define an async function to allow awaiting.
|
||||
4. Get the miner.
|
||||
5. Make sure the miner exists.
|
||||
If this result is `None`, the miner may be offline.
|
||||
6. Get data from the miner.
|
||||
7. All the data from the dataclass.
|
||||
8. Hashrate of the miner, with unit information.
|
||||
9. Get the miner data asynchronously with asyncio.run().
|
||||
|
||||
### Multiple miners
|
||||
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
|
||||
```python
|
||||
import asyncio # asyncio for handling the async part
|
||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
||||
import asyncio# (1)!
|
||||
from pyasic.network import MinerNetwork# (2)!
|
||||
|
||||
|
||||
async def gather_miner_data(): # define async scan function to allow awaiting
|
||||
network = MinerNetwork.from_subnet("192.168.1.50/24")
|
||||
miners = await network.scan()
|
||||
async def gather_miner_data():# (3)!
|
||||
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
||||
miners = await network.scan()# (5)!
|
||||
|
||||
# we need to asyncio.gather() all the miners get_data() functions to make them run together
|
||||
all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners])
|
||||
|
||||
for miner_data in all_miner_data:
|
||||
print(miner_data) # print out all the data one by one
|
||||
print(miner_data)# (7)!
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(gather_miner_data())
|
||||
asyncio.run(gather_miner_data())# (8)!
|
||||
```
|
||||
|
||||
---
|
||||
1. `asyncio` for handling the async part.
|
||||
2. `MinerNetwork` handles the scanning.
|
||||
3. Define an async function to allow awaiting.
|
||||
4. Create a miner network.
|
||||
5. Scan for miners asynchronously.
|
||||
6. Use `asyncio.gather()` with all the miners' `get_data()` functions to make them run together.
|
||||
7. Print out the data one at a time.
|
||||
8. Get the miner data asynchronously with asyncio.run().
|
||||
|
||||
## Miner control
|
||||
---
|
||||
`pyasic` exposes a standard interface for each miner using control functions.
|
||||
Every miner class in `pyasic` must implement all the control functions defined in [`MinerProtocol`][pyasic.miners.base.MinerProtocol].
|
||||
Every miner class in `pyasic` must implement all the following control functions.
|
||||
|
||||
These functions are
|
||||
[`check_light`][pyasic.miners.base.MinerProtocol.check_light],
|
||||
[`fault_light_off`][pyasic.miners.base.MinerProtocol.fault_light_off],
|
||||
[`fault_light_on`][pyasic.miners.base.MinerProtocol.fault_light_on],
|
||||
@@ -166,35 +170,41 @@ These functions are
|
||||
[`send_config`][pyasic.miners.base.MinerProtocol.send_config], and
|
||||
[`set_power_limit`][pyasic.miners.base.MinerProtocol.set_power_limit].
|
||||
|
||||
##### Usage
|
||||
### Usage
|
||||
```python
|
||||
import asyncio
|
||||
from pyasic import get_miner
|
||||
import asyncio# (1)!
|
||||
from pyasic import get_miner# (2)!
|
||||
|
||||
|
||||
async def set_fault_light():
|
||||
miner = await get_miner("192.168.1.20")
|
||||
async def set_fault_light():# (3)!
|
||||
miner = await get_miner("192.168.1.20")# (4)!
|
||||
|
||||
# call control function
|
||||
await miner.fault_light_on()
|
||||
await miner.fault_light_on()# (5)!
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(set_fault_light())
|
||||
asyncio.run(set_fault_light())# (6)!
|
||||
```
|
||||
|
||||
---
|
||||
1. `asyncio` for handling the async part.
|
||||
2. `get_miner` handles the miner type selection.
|
||||
3. Define an async function to allow awaiting.
|
||||
4. Get the miner.
|
||||
5. Call the miner control function.
|
||||
6. Call the control function asynchronously with asyncio.run().
|
||||
|
||||
|
||||
## Helper dataclasses
|
||||
---
|
||||
|
||||
##### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
||||
### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
||||
|
||||
`pyasic` implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
||||
|
||||
---
|
||||
|
||||
##### [`MinerData`][pyasic.data.MinerData]
|
||||
### [`MinerData`][pyasic.data.MinerData]
|
||||
|
||||
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns.
|
||||
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`][pyasic.miners.base.MinerProtocol.get_data] function, and is used to have a consistent dataset across all returns.
|
||||
|
||||
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
||||
|
||||
@@ -213,13 +223,13 @@ average_data = sum(list_of_miner_data, start=MinerData("0.0.0.0"))/len(list_of_m
|
||||
|
||||
---
|
||||
|
||||
##### [`MinerConfig`][pyasic.config.MinerConfig]
|
||||
### [`MinerConfig`][pyasic.config.MinerConfig]
|
||||
|
||||
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
||||
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`](#get-config).
|
||||
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`][pyasic.miners.base.MinerProtocol.get_config].
|
||||
|
||||
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
||||
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
||||
In most cases these helper functions should not be used, as [`send_config()`][pyasic.miners.base.MinerProtocol.send_config] takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
||||
|
||||
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
||||
```python
|
||||
@@ -241,7 +251,6 @@ if __name__ == "__main__":
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
## Settings
|
||||
---
|
||||
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows:
|
||||
@@ -252,7 +261,7 @@ from pyasic import settings
|
||||
settings.update("default_antminer_web_password", "my_pwd")
|
||||
```
|
||||
|
||||
##### Default values:
|
||||
### Default values:
|
||||
```
|
||||
"network_ping_retries": 1,
|
||||
"network_ping_timeout": 3,
|
||||
|
||||
@@ -10,8 +10,3 @@ You may not instantiate this class on its own, only subclass from it.
|
||||
handler: python
|
||||
options:
|
||||
heading_level: 4
|
||||
|
||||
::: pyasic.miners.base.MinerProtocol
|
||||
handler: python
|
||||
options:
|
||||
heading_level: 4
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
jinja2<3.1.4
|
||||
mkdocs
|
||||
mkdocstrings[python]
|
||||
zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
Reference in New Issue
Block a user