Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6c87a864d | ||
|
|
ed17a0f436 | ||
|
|
36fead3dd1 | ||
|
|
ecb16c10ca | ||
|
|
a540db3246 | ||
|
|
81a2f99fbf | ||
|
|
1dd9f742ad | ||
|
|
7bd6a0f136 | ||
|
|
7297f12e88 | ||
|
|
0e009c3a16 | ||
|
|
95b0cc364b | ||
|
|
2dcc4f0cfc | ||
|
|
d7e9498018 | ||
|
|
0324a21e79 | ||
|
|
5700bd1c9c | ||
|
|
abc6494f18 | ||
|
|
5de8fc064e | ||
|
|
c9d620105b | ||
|
|
5d6fc5b26d | ||
|
|
6bd319355d | ||
|
|
31827e7dd1 | ||
|
|
26961a5d8c | ||
|
|
2ff09a3765 | ||
|
|
18c26adbb6 | ||
|
|
4bfafabe9d | ||
|
|
19e6ed90ec | ||
|
|
eca60d1eae | ||
|
|
8b8a592308 | ||
|
|
c3de4188d6 | ||
|
|
490138fd1a | ||
|
|
f566b7fcb9 | ||
|
|
7fb4237e51 | ||
|
|
eeffdecde1 | ||
|
|
477a411c87 |
@@ -1,8 +0,0 @@
|
|||||||
# Ignore VENV
|
|
||||||
venv
|
|
||||||
|
|
||||||
# Ignore builds
|
|
||||||
build
|
|
||||||
|
|
||||||
# Ignore github files
|
|
||||||
.github
|
|
||||||
22
.github/workflows/python-publish.yml
vendored
Normal file
22
.github/workflows/python-publish.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: PyPI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'docsrc/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Publish GH release
|
||||||
|
uses: softprops/action-gh-release@v0.1.14
|
||||||
|
- name: Build using poetry and publish to PyPi
|
||||||
|
uses: JRubics/poetry-publish@v1.11
|
||||||
|
with:
|
||||||
|
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
||||||
20
.readthedocs.yaml
Normal file
20
.readthedocs.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# .readthedocs.yaml
|
||||||
|
# Read the Docs configuration file
|
||||||
|
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||||
|
|
||||||
|
# Required
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
# Set the version of Python and other tools you might need
|
||||||
|
build:
|
||||||
|
os: ubuntu-20.04
|
||||||
|
tools:
|
||||||
|
python: "3.9"
|
||||||
|
|
||||||
|
mkdocs:
|
||||||
|
configuration: mkdocs.yml
|
||||||
|
|
||||||
|
# Optionally declare the Python requirements required to build your docs
|
||||||
|
python:
|
||||||
|
install:
|
||||||
|
- requirements: docs/requirements.txt
|
||||||
307
README.md
307
README.md
@@ -2,16 +2,25 @@
|
|||||||
*A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH.*
|
*A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH.*
|
||||||
|
|
||||||
[](https://github.com/psf/black)
|
[](https://github.com/psf/black)
|
||||||
|
[](https://pypi.org/project/pyasic/)
|
||||||
|
[](https://pypi.org/project/pyasic/)
|
||||||
|
[](https://pyasic.readthedocs.io/en/latest/)
|
||||||
|
## Documentation
|
||||||
|
Documentation is located on Read the Docs as [pyasic](https://pyasic.readthedocs.io/en/latest/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Standard Usage
|
||||||
|
You can install pyasic directly from pip with the command `pip install pyasic`
|
||||||
|
|
||||||
|
For those of you who aren't comfortable with code and developer tools, there are windows builds of GUI applications that use this library here -> (https://drive.google.com/drive/folders/1DjR8UOS_g0ehfiJcgmrV0FFoqFvE9akW?usp=sharing)
|
||||||
|
|
||||||
|
### Developers
|
||||||
To use this repo, first download it, create a virtual environment, enter the virtual environment, and install relevant packages by navigating to this directory and running ```pip install -r requirements.txt``` on Windows or ```pip3 install -r requirements.txt``` on Mac or UNIX if the first command fails.
|
To use this repo, first download it, create a virtual environment, enter the virtual environment, and install relevant packages by navigating to this directory and running ```pip install -r requirements.txt``` on Windows or ```pip3 install -r requirements.txt``` on Mac or UNIX if the first command fails.
|
||||||
|
|
||||||
You can also use poetry by initializing and running ```poetry install```
|
You can also use poetry by initializing and running ```poetry install```
|
||||||
|
|
||||||
For those of you who aren't comfortable with code and developer tools, there are windows builds of the GUI applications here -> (https://drive.google.com/drive/folders/1DjR8UOS_g0ehfiJcgmrV0FFoqFvE9akW?usp=sharing)
|
|
||||||
|
|
||||||
### Interfacing with miners programmatically
|
### Interfacing with miners programmatically
|
||||||
<br>
|
|
||||||
|
|
||||||
##### Note: If you are trying to interface with Whatsminers, there is a bug in the way they are interacted with on Windows, so to fix that you need to change the event loop policy using this code:
|
##### Note: If you are trying to interface with Whatsminers, there is a bug in the way they are interacted with on Windows, so to fix that you need to change the event loop policy using this code:
|
||||||
```python
|
```python
|
||||||
@@ -31,228 +40,130 @@ To write your own custom programs with this repo, you have many options.
|
|||||||
|
|
||||||
It is recommended that you explore the files in this repo to familiarize yourself with them, try starting with the miners module and going from there.
|
It is recommended that you explore the files in this repo to familiarize yourself with them, try starting with the miners module and going from there.
|
||||||
|
|
||||||
A basic script to find all miners on the network and get the hashrate from them looks like this -
|
There are 2 main ways to get a miner and it's functions via scanning or via the MinerFactory.
|
||||||
|
|
||||||
|
#### Scanning for miners
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import sys
|
||||||
|
|
||||||
from pyasic.network import MinerNetwork
|
from pyasic.network import MinerNetwork
|
||||||
|
|
||||||
|
# Fix whatsminer bug
|
||||||
|
# if the computer is windows, set the event loop policy to a WindowsSelector policy
|
||||||
|
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
|
||||||
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
|
|
||||||
async def get_hashrate():
|
|
||||||
# Miner Network class allows for easy scanning of a network
|
# define asynchronous function to scan for miners
|
||||||
# Give it any IP on a network and it will find the whole subnet
|
async def scan_and_get_data():
|
||||||
# It can also be passed a subnet mask:
|
# Define network range to be used for scanning
|
||||||
# miner_network = MinerNetwork('192.168.1.55', mask=23)
|
# This can take a list of IPs, a constructor string, or an IP and subnet mask
|
||||||
miner_network = MinerNetwork('192.168.1.1')
|
# The standard mask is /24, and you can pass any IP address in the subnet
|
||||||
# Miner Network scan function returns Miner classes for all miners found
|
net = MinerNetwork("192.168.1.69", mask=24)
|
||||||
miners = await miner_network.scan_network_for_miners()
|
# Scan the network for miners
|
||||||
# Each miner will return with its own set of functions, and an API class instance
|
# This function returns a list of miners of the correct type as a class
|
||||||
|
miners: list = await net.scan_network_for_miners()
|
||||||
|
|
||||||
|
# We can now get data from any of these miners
|
||||||
|
# To do them all we have to create a list of tasks and gather them
|
||||||
tasks = [miner.get_data() for miner in miners]
|
tasks = [miner.get_data() for miner in miners]
|
||||||
# Gather all tasks asynchronously and run them
|
# Gather all tasks asynchronously and run them
|
||||||
data = await asyncio.gather(*tasks)
|
data = await asyncio.gather(*tasks)
|
||||||
# now we have a list of MinerData, and can get .hashrate
|
|
||||||
print([item.hashrate for item in data])
|
# Data is now a list of MinerData, and we can reference any part of that
|
||||||
|
# Print out all data for now
|
||||||
|
for item in data:
|
||||||
if __name__ == '__main__':
|
print(item)
|
||||||
asyncio.new_event_loop().run_until_complete(get_hashrate())
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(scan_and_get_data())
|
||||||
```
|
```
|
||||||
<br>
|
|
||||||
You can also create your own miner without scanning if you know the IP:
|
|
||||||
|
|
||||||
|
</br>
|
||||||
|
|
||||||
|
#### Getting a miner if you know the IP
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
import ipaddress
|
import sys
|
||||||
from pyasic.miners.miner_factory import Mine~~~~rFactory
|
|
||||||
|
from pyasic.miners.miner_factory import MinerFactory
|
||||||
|
|
||||||
|
# Fix whatsminer bug
|
||||||
|
# if the computer is windows, set the event loop policy to a WindowsSelector policy
|
||||||
|
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
|
||||||
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
|
|
||||||
|
|
||||||
async def get_miner_hashrate(ip: str):
|
# define asynchronous function to get miner and data
|
||||||
# Instantiate a Miner Factory to generate miners from their IP
|
async def get_miner_data(miner_ip: str):
|
||||||
miner_factory = MinerFactory()
|
# Use MinerFactory to get miner
|
||||||
# Make the string IP into an IP address
|
# MinerFactory is a singleton, so we can just get the instance in place
|
||||||
miner_ip = ipaddress.ip_address(ip)
|
miner = await MinerFactory().get_miner(miner_ip)
|
||||||
# Wait for the factory to return the miner
|
|
||||||
miner = await miner_factory.get_miner(miner_ip)
|
# Get data from the miner
|
||||||
# Get the API data
|
|
||||||
data = await miner.get_data()
|
data = await miner.get_data()
|
||||||
# print out hashrate
|
|
||||||
print(data.hashrate)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
asyncio.new_event_loop().run_until_complete(
|
|
||||||
get_miner_hashrate(str("192.168.1.69")))
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Now that you know that, lets move on to some common API functions that you might want to use.
|
|
||||||
|
|
||||||
### Common commands:
|
|
||||||
* Get the data used by the config utility, this includes pool data, wattage use, temperature, hashrate, etc:
|
|
||||||
* All the data from below commands and more are returned from this in a consistent dataclass. Check out the `MinerData` class in `/data/__init__.py` for more information.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
import ipaddress
|
|
||||||
from pyasic.miners.miner_factory import MinerFactory
|
|
||||||
|
|
||||||
|
|
||||||
async def get_miner_pool_data(ip: str):
|
|
||||||
# Instantiate a Miner Factory to generate miners from their IP
|
|
||||||
miner_factory = MinerFactory()
|
|
||||||
# Make the string IP into an IP address
|
|
||||||
miner_ip = ipaddress.ip_address(ip)
|
|
||||||
# Wait for the factory to return the miner
|
|
||||||
miner = await miner_factory.get_miner(miner_ip)
|
|
||||||
# Get the data
|
|
||||||
data = await miner.get_data()
|
|
||||||
|
|
||||||
print(data)
|
print(data)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
if __name__ == '__main__':
|
asyncio.run(get_miner_data("192.168.1.69"))
|
||||||
asyncio.new_event_loop().run_until_complete(
|
|
||||||
get_miner_pool_data(str("192.168.1.69")))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Advanced data gathering
|
||||||
|
|
||||||
|
If needed, this library exposes a wrapper for the miner API that can be used for advanced data gathering.
|
||||||
|
|
||||||
|
#### List available API commands
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from pyasic.miners.miner_factory import MinerFactory
|
||||||
|
|
||||||
|
# Fix whatsminer bug
|
||||||
|
# if the computer is windows, set the event loop policy to a WindowsSelector policy
|
||||||
|
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
|
||||||
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
|
|
||||||
|
|
||||||
* Getting pool data:
|
async def get_api_commands(miner_ip: str):
|
||||||
|
# Get the miner
|
||||||
|
miner = await MinerFactory().get_miner(miner_ip)
|
||||||
|
|
||||||
|
# List all available commands
|
||||||
|
print(miner.api.get_commands())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(get_api_commands("192.168.1.69"))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Use miner API commands to gather data
|
||||||
|
|
||||||
|
The miner API commands will raise an `APIError` if they fail with a bad status code, to bypass this you must send them manually by using `miner.api.send_command(command, ignore_errors=True)`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
import ipaddress
|
import sys
|
||||||
|
|
||||||
from pyasic.miners.miner_factory import MinerFactory
|
from pyasic.miners.miner_factory import MinerFactory
|
||||||
|
|
||||||
|
# Fix whatsminer bug
|
||||||
async def get_miner_pool_data(ip: str):
|
# if the computer is windows, set the event loop policy to a WindowsSelector policy
|
||||||
# Instantiate a Miner Factory to generate miners from their IP
|
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
|
||||||
miner_factory = MinerFactory()
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
# Make the string IP into an IP address
|
|
||||||
miner_ip = ipaddress.ip_address(ip)
|
|
||||||
# Wait for the factory to return the miner
|
|
||||||
miner = await miner_factory.get_miner(miner_ip)
|
|
||||||
# Get the API data
|
|
||||||
pools = await miner.api.pools()
|
|
||||||
# safe_parse_api_data parses the data from a miner API
|
|
||||||
# It will raise an APIError (from API import APIError) if there is a problem
|
|
||||||
data = pools["POOLS"]
|
|
||||||
# parse further from here to get all the pool info you want.
|
|
||||||
# each pool is on a different index eg:
|
|
||||||
# data[0] is pool 1
|
|
||||||
# data[1] is pool 2
|
|
||||||
# etc
|
|
||||||
print(data)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
async def get_api_commands(miner_ip: str):
|
||||||
asyncio.new_event_loop().run_until_complete(
|
# Get the miner
|
||||||
get_miner_pool_data(str("192.168.1.69")))
|
miner = await MinerFactory().get_miner(miner_ip)
|
||||||
```
|
|
||||||
|
# Run the devdetails command
|
||||||
* Getting temperature data:
|
# This is equivalent to await miner.api.send_command("devdetails")
|
||||||
|
devdetails: dict = await miner.api.devdetails()
|
||||||
This one is a bit tougher, lots of miners do this a different way, you might need to experiment a bit to find what works for you.
|
print(devdetails)
|
||||||
BraiinsOS uses the "temps" command, Whatsminers has it in "devs", Avalonminers put it in "stats" as well as some other miners,
|
|
||||||
but the spot I like to try first is in "summary".
|
|
||||||
|
if __name__ == "__main__":
|
||||||
A pretty good example of really trying to make this robust is in ```cfg_util.func.miners``` in the ```get_formatted_data()``` function.
|
asyncio.run(get_api_commands("192.168.1.69"))
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
import ipaddress
|
|
||||||
from pyasic.miners.miner_factory import MinerFactory
|
|
||||||
|
|
||||||
|
|
||||||
async def get_miner_temperature_data(ip: str):
|
|
||||||
# Instantiate a Miner Factory to generate miners from their IP
|
|
||||||
miner_factory = MinerFactory()
|
|
||||||
# Make the string IP into an IP address
|
|
||||||
miner_ip = ipaddress.ip_address(ip)
|
|
||||||
# Wait for the factory to return the miner
|
|
||||||
miner = await miner_factory.get_miner(miner_ip)
|
|
||||||
# Get the API data
|
|
||||||
summary = await miner.api.summary()
|
|
||||||
|
|
||||||
data = summary['SUMMARY'][0]["Temperature"]
|
|
||||||
print(data)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
asyncio.new_event_loop().run_until_complete(
|
|
||||||
get_miner_temperature_data(str("192.168.1.69")))
|
|
||||||
```
|
|
||||||
|
|
||||||
* Getting power data:
|
|
||||||
|
|
||||||
How about data on the power usage of the miner? This one only works for Whatsminers and BraiinsOS for now, and the Braiins one just uses the tuning setting, but its good enough for basic uses.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
import ipaddress
|
|
||||||
from pyasic.miners.miner_factory import MinerFactory
|
|
||||||
|
|
||||||
|
|
||||||
async def get_miner_power_data(ip: str):
|
|
||||||
data = None
|
|
||||||
# Instantiate a Miner Factory to generate miners from their IP
|
|
||||||
miner_factory = MinerFactory()
|
|
||||||
# Make the string IP into an IP address
|
|
||||||
miner_ip = ipaddress.ip_address(ip)
|
|
||||||
# Wait for the factory to return the miner
|
|
||||||
miner = await miner_factory.get_miner(miner_ip)
|
|
||||||
# check if this can be sent the "tunerstatus" command, BraiinsOS only
|
|
||||||
if "tunerstatus" in miner.api.get_commands():
|
|
||||||
# send the command
|
|
||||||
tunerstatus = await miner.api.tunerstatus()
|
|
||||||
# parse the return
|
|
||||||
data = tunerstatus['TUNERSTATUS'][0]["PowerLimit"]
|
|
||||||
else:
|
|
||||||
# send the command
|
|
||||||
# whatsminers have the power info in summary
|
|
||||||
summary = await miner.api.summary()
|
|
||||||
# parse the return
|
|
||||||
data = summary['SUMMARY'][0]["Power"]
|
|
||||||
|
|
||||||
if data:
|
|
||||||
print(data)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
asyncio.new_event_loop().run_until_complete(
|
|
||||||
get_miner_power_data(str("192.168.1.69")))
|
|
||||||
```
|
|
||||||
|
|
||||||
* Multicommands:
|
|
||||||
|
|
||||||
Multicommands make it much easier to get many types of data all at once. The multicommand function will also remove any commands that your API can't handle automatically.
|
|
||||||
How about we get the current pool user and hashrate in 1 command?
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
import ipaddress
|
|
||||||
from pyasic.miners.miner_factory import MinerFactory
|
|
||||||
from tools.cfg_util_old.func.parse_data import safe_parse_api_data
|
|
||||||
|
|
||||||
|
|
||||||
async def get_miner_hashrate_and_pool(ip: str):
|
|
||||||
# Instantiate a Miner Factory to generate miners from their IP
|
|
||||||
miner_factory = MinerFactory()
|
|
||||||
# Make the string IP into an IP address
|
|
||||||
miner_ip = ipaddress.ip_address(ip)
|
|
||||||
# Wait for the factory to return the miner
|
|
||||||
miner = await miner_factory.get_miner(miner_ip)
|
|
||||||
# Get the API data
|
|
||||||
api_data = await miner.api.multicommand("pools", "summary")
|
|
||||||
if "pools" in api_data.keys():
|
|
||||||
user = api_data["pools"][0]["POOLS"][0]["User"]
|
|
||||||
print(user)
|
|
||||||
if "summary" in api_data.keys():
|
|
||||||
hashrate = api_data["summary"][0]["SUMMARY"][0]["MHS av"]
|
|
||||||
print(hashrate)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
asyncio.new_event_loop().run_until_complete(
|
|
||||||
get_miner_hashrate_and_pool(str("192.168.1.9")))
|
|
||||||
```
|
```
|
||||||
|
|||||||
42
docs/api.md
Normal file
42
docs/api.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# pyasic
|
||||||
|
## Miner APIs
|
||||||
|
Each miner has a unique API that is used to communicate with it.
|
||||||
|
Each of these API types has commands that differ between them, and some commands have data that others do not.
|
||||||
|
Each miner that is a subclass of `BaseMiner` should have an API linked to it as `Miner.api`.
|
||||||
|
|
||||||
|
All API implementations inherit from `BaseMinerAPI`, which implements the basic communications protocols.
|
||||||
|
|
||||||
|
## BMMinerAPI
|
||||||
|
::: pyasic.API.bmminer.BMMinerAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## BOSMinerAPI
|
||||||
|
::: pyasic.API.bosminer.BOSMinerAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## BTMinerAPI
|
||||||
|
::: pyasic.API.btminer.BTMinerAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## CGMinerAPI
|
||||||
|
::: pyasic.API.cgminer.CGMinerAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## UnknownAPI
|
||||||
|
::: pyasic.API.unknown.UnknownAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
99
docs/index.md
Normal file
99
docs/index.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# pyasic
|
||||||
|
*A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH.*
|
||||||
|
|
||||||
|
[](https://github.com/psf/black)
|
||||||
|
[](https://pypi.org/project/pyasic/)
|
||||||
|
[](https://pypi.org/project/pyasic/)
|
||||||
|
[](https://pyasic.readthedocs.io/en/latest/)
|
||||||
|
|
||||||
|
## Intro
|
||||||
|
Welcome to pyasic! Pyasic uses an asynchronous method of communicating with asic miners on your network, which makes it super fast.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## 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_network_for_miners()`][pyasic.network.MinerNetwork.scan_network_for_miners] 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
|
||||||
|
|
||||||
|
|
||||||
|
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("192.168.1.50") # this uses the 192.168.1.0-255 network
|
||||||
|
|
||||||
|
# scan for miners asynchronously
|
||||||
|
# this will return the correct type of miners if they are supported with all functionality.
|
||||||
|
miners = await network.scan_network_for_miners()
|
||||||
|
print(miners)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(scan_miners()) # run the scan asynchronously with asyncio.run()
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Creating miners based on IP
|
||||||
|
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.miner_factory.MinerFactory] to communicate and identify the miners.
|
||||||
|
The function [`MinerFactory().get_miner()`][pyasic.miners.miner_factory.MinerFactory.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.miners.miner_factory import MinerFactory # miner factory handles miners creation
|
||||||
|
|
||||||
|
|
||||||
|
async def get_miners(): # define async scan function to allow awaiting
|
||||||
|
# get the miner with miner factory
|
||||||
|
# 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()
|
||||||
|
miner_1 = await MinerFactory().get_miner("192.168.1.75")
|
||||||
|
miner_2 = await MinerFactory().get_miner("192.168.1.76")
|
||||||
|
print(miner_1, miner_2)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run()
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Getting data from miners
|
||||||
|
|
||||||
|
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 a 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].
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
from pyasic.miners.miner_factory import MinerFactory
|
||||||
|
|
||||||
|
async def gather_miner_data():
|
||||||
|
miner = await MinerFactory().get_miner("192.168.1.75")
|
||||||
|
miner_data = await miner.get_data()
|
||||||
|
print(miner_data) # all data from the dataclass
|
||||||
|
print(miner_data.hashrate) # hashrate of the miner in TH/s
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(gather_miner_data())
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
async def gather_miner_data(): # define async scan function to allow awaiting
|
||||||
|
network = MinerNetwork("192.168.1.50")
|
||||||
|
miners = await network.scan_network_for_miners()
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(gather_miner_data())
|
||||||
|
```
|
||||||
8
docs/miner_data.md
Normal file
8
docs/miner_data.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# pyasic
|
||||||
|
## Miner Data
|
||||||
|
|
||||||
|
::: pyasic.data.MinerData
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
8
docs/miner_factory.md
Normal file
8
docs/miner_factory.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# pyasic
|
||||||
|
## Miner Factory
|
||||||
|
|
||||||
|
::: pyasic.miners.miner_factory.MinerFactory
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
8
docs/miner_network.md
Normal file
8
docs/miner_network.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# pyasic
|
||||||
|
## Miner Network
|
||||||
|
|
||||||
|
::: pyasic.network.MinerNetwork
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
jinja2<3.1.0
|
||||||
|
mkdocs
|
||||||
|
mkdocstrings[python]
|
||||||
14
mkdocs.yml
Normal file
14
mkdocs.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
site_name: pyasic
|
||||||
|
repo_url: https://github.com/UpstreamData/pyasic
|
||||||
|
nav:
|
||||||
|
- Introduction: "index.md"
|
||||||
|
- Usage:
|
||||||
|
- Miner Factory: "miner_factory.md"
|
||||||
|
- Miner Network: "miner_network.md"
|
||||||
|
- Miner Data: "miner_data.md"
|
||||||
|
- Advanced:
|
||||||
|
- API: "api.md"
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- mkdocstrings
|
||||||
|
- search
|
||||||
@@ -6,17 +6,17 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
Each method corresponds to an API command in BMMiner.
|
Each method corresponds to an API command in BMMiner.
|
||||||
|
|
||||||
BMMiner API documentation:
|
[BMMiner API documentation](https://github.com/jameshilliard/bmminer/blob/master/API-README)
|
||||||
https://github.com/jameshilliard/bmminer/blob/master/API-README
|
|
||||||
|
|
||||||
This class abstracts use of the BMMiner API, as well as the
|
This class abstracts use of the BMMiner API, as well as the
|
||||||
methods for sending commands to it. The self.send_command()
|
methods for sending commands to it. The `self.send_command()`
|
||||||
function handles sending a command to the miner asynchronously, and
|
function handles sending a command to the miner asynchronously, and
|
||||||
as such is the base for many of the functions in this class, which
|
as such is the base for many of the functions in this class, which
|
||||||
rely on it to send the command for them.
|
rely on it to send the command for them.
|
||||||
|
|
||||||
:param ip: The IP of the miner to reference the API on.
|
Parameters:
|
||||||
:param port: The port to reference the API on. Default is 4028.
|
ip: The IP of the miner to reference the API on.
|
||||||
|
port: The port to reference the API on. Default is 4028.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip: str, port: int = 4028) -> None:
|
def __init__(self, ip: str, port: int = 4028) -> None:
|
||||||
@@ -24,58 +24,81 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def version(self) -> dict:
|
async def version(self) -> dict:
|
||||||
"""Get miner version info.
|
"""Get miner version info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Miner version information.
|
Returns:
|
||||||
|
Miner version information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("version")
|
return await self.send_command("version")
|
||||||
|
|
||||||
async def config(self) -> dict:
|
async def config(self) -> dict:
|
||||||
"""Get some basic configuration info.
|
"""Get some basic configuration info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Some miner configuration information:
|
Returns:
|
||||||
ASC Count <- the number of ASCs
|
## Some miner configuration information:
|
||||||
PGA Count <- the number of PGAs
|
* ASC Count <- the number of ASCs
|
||||||
Pool Count <- the number of Pools
|
* PGA Count <- the number of PGAs
|
||||||
Strategy <- the current pool strategy
|
* Pool Count <- the number of Pools
|
||||||
Log Interval <- the interval of logging
|
* Strategy <- the current pool strategy
|
||||||
Device Code <- list of compiled device drivers
|
* Log Interval <- the interval of logging
|
||||||
OS <- the current operating system
|
* Device Code <- list of compiled device drivers
|
||||||
Failover-Only <- failover-only setting
|
* OS <- the current operating system
|
||||||
Scan Time <- scan-time setting
|
* Failover-Only <- failover-only setting
|
||||||
Queue <- queue setting
|
* Scan Time <- scan-time setting
|
||||||
Expiry <- expiry setting
|
* Queue <- queue setting
|
||||||
|
* Expiry <- expiry setting
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("config")
|
return await self.send_command("config")
|
||||||
|
|
||||||
async def summary(self) -> dict:
|
async def summary(self) -> dict:
|
||||||
"""Get the status summary of the miner.
|
"""Get the status summary of the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The status summary of the miner.
|
Returns:
|
||||||
|
The status summary of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("summary")
|
return await self.send_command("summary")
|
||||||
|
|
||||||
async def pools(self) -> dict:
|
async def pools(self) -> dict:
|
||||||
"""Get pool information.
|
"""Get pool information.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Miner pool information.
|
Returns:
|
||||||
|
Miner pool information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pools")
|
return await self.send_command("pools")
|
||||||
|
|
||||||
async def devs(self) -> dict:
|
async def devs(self) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details.
|
"""Get data on each PGA/ASC with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devs")
|
return await self.send_command("devs")
|
||||||
|
|
||||||
async def edevs(self, old: bool = False) -> dict:
|
async def edevs(self, old: bool = False) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details, ignoring
|
"""Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
|
||||||
blacklisted and zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param old: Include zombie devices that became zombies less
|
Parameters:
|
||||||
than 'old' seconds ago
|
old: Include zombie devices that became zombies less than 'old' seconds ago
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if old:
|
if old:
|
||||||
return await self.send_command("edevs", parameters=old)
|
return await self.send_command("edevs", parameters=old)
|
||||||
@@ -84,46 +107,70 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def pga(self, n: int) -> dict:
|
async def pga(self, n: int) -> dict:
|
||||||
"""Get data from PGA n.
|
"""Get data from PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA number to get data from.
|
Parameters:
|
||||||
|
n: The PGA number to get data from.
|
||||||
|
|
||||||
:return: Data on the PGA n.
|
Returns:
|
||||||
|
Data on the PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pga", parameters=n)
|
return await self.send_command("pga", parameters=n)
|
||||||
|
|
||||||
async def pgacount(self) -> dict:
|
async def pgacount(self) -> dict:
|
||||||
"""Get data fon all PGAs.
|
"""Get data fon all PGAs.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on the PGAs connected.
|
Returns:
|
||||||
|
Data on the PGAs connected.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgacount")
|
return await self.send_command("pgacount")
|
||||||
|
|
||||||
async def switchpool(self, n: int) -> dict:
|
async def switchpool(self, n: int) -> dict:
|
||||||
"""Switch pools to pool n.
|
"""Switch pools to pool n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The pool to switch to.
|
Parameters:
|
||||||
|
n: The pool to switch to.
|
||||||
|
|
||||||
:return: A confirmation of switching to pool n.
|
Returns:
|
||||||
|
A confirmation of switching to pool n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("switchpool", parameters=n)
|
return await self.send_command("switchpool", parameters=n)
|
||||||
|
|
||||||
async def enablepool(self, n: int) -> dict:
|
async def enablepool(self, n: int) -> dict:
|
||||||
"""Enable pool n.
|
"""Enable pool n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The pool to enable.
|
Parameters:
|
||||||
|
n: The pool to enable.
|
||||||
|
|
||||||
:return: A confirmation of enabling pool n.
|
Returns:
|
||||||
|
A confirmation of enabling pool n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("enablepool", parameters=n)
|
return await self.send_command("enablepool", parameters=n)
|
||||||
|
|
||||||
async def addpool(self, url: str, username: str, password: str) -> dict:
|
async def addpool(self, url: str, username: str, password: str) -> dict:
|
||||||
"""Add a pool to the miner.
|
"""Add a pool to the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param url: The URL of the new pool to add.
|
Parameters:
|
||||||
:param username: The users username on the new pool.
|
url: The URL of the new pool to add.
|
||||||
:param password: The worker password on the new pool.
|
username: The users username on the new pool.
|
||||||
|
password: The worker password on the new pool.
|
||||||
|
|
||||||
:return: A confirmation of adding the pool.
|
Returns:
|
||||||
|
A confirmation of adding the pool.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command(
|
return await self.send_command(
|
||||||
"addpool", parameters=f"{url},{username},{password}"
|
"addpool", parameters=f"{url},{username},{password}"
|
||||||
@@ -131,48 +178,73 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def poolpriority(self, *n: int) -> dict:
|
async def poolpriority(self, *n: int) -> dict:
|
||||||
"""Set pool priority.
|
"""Set pool priority.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pools in order of priority.
|
Parameters:
|
||||||
|
*n: Pools in order of priority.
|
||||||
|
|
||||||
:return: A confirmation of setting pool priority.
|
Returns:
|
||||||
|
A confirmation of setting pool priority.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
pools = f"{','.join([str(item) for item in n])}"
|
pools = f"{','.join([str(item) for item in n])}"
|
||||||
return await self.send_command("poolpriority", parameters=pools)
|
return await self.send_command("poolpriority", parameters=pools)
|
||||||
|
|
||||||
async def poolquota(self, n: int, q: int) -> dict:
|
async def poolquota(self, n: int, q: int) -> dict:
|
||||||
"""Set pool quota.
|
"""Set pool quota.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pool number to set quota on.
|
Parameters:
|
||||||
:param q: Quota to set the pool to.
|
n: Pool number to set quota on.
|
||||||
|
q: Quota to set the pool to.
|
||||||
|
|
||||||
:return: A confirmation of setting pool quota.
|
Returns:
|
||||||
|
A confirmation of setting pool quota.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("poolquota", parameters=f"{n},{q}")
|
return await self.send_command("poolquota", parameters=f"{n},{q}")
|
||||||
|
|
||||||
async def disablepool(self, n: int) -> dict:
|
async def disablepool(self, n: int) -> dict:
|
||||||
"""Disable a pool.
|
"""Disable a pool.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pool to disable.
|
Parameters:
|
||||||
|
n: Pool to disable.
|
||||||
|
|
||||||
:return: A confirmation of diabling the pool.
|
Returns:
|
||||||
|
A confirmation of diabling the pool.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("disablepool", parameters=n)
|
return await self.send_command("disablepool", parameters=n)
|
||||||
|
|
||||||
async def removepool(self, n: int) -> dict:
|
async def removepool(self, n: int) -> dict:
|
||||||
"""Remove a pool.
|
"""Remove a pool.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pool to remove.
|
Parameters:
|
||||||
|
n: Pool to remove.
|
||||||
|
|
||||||
:return: A confirmation of removing the pool.
|
Returns:
|
||||||
|
A confirmation of removing the pool.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("removepool", parameters=n)
|
return await self.send_command("removepool", parameters=n)
|
||||||
|
|
||||||
async def save(self, filename: str = None) -> dict:
|
async def save(self, filename: str = None) -> dict:
|
||||||
"""Save the config.
|
"""Save the config.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param filename: Filename to save the config as.
|
Parameters:
|
||||||
|
filename: Filename to save the config as.
|
||||||
|
|
||||||
:return: A confirmation of saving the config.
|
Returns:
|
||||||
|
A confirmation of saving the config.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if filename:
|
if filename:
|
||||||
return await self.send_command("save", parameters=filename)
|
return await self.send_command("save", parameters=filename)
|
||||||
@@ -181,83 +253,123 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def quit(self) -> dict:
|
async def quit(self) -> dict:
|
||||||
"""Quit BMMiner.
|
"""Quit BMMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: A single "BYE" before BMMiner quits.
|
Returns:
|
||||||
|
A single "BYE" before BMMiner quits.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("quit")
|
return await self.send_command("quit")
|
||||||
|
|
||||||
async def notify(self) -> dict:
|
async def notify(self) -> dict:
|
||||||
"""Notify the user of past errors.
|
"""Notify the user of past errors.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The last status and count of each devices problem(s).
|
Returns:
|
||||||
|
The last status and count of each devices problem(s).
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("notify")
|
return await self.send_command("notify")
|
||||||
|
|
||||||
async def privileged(self) -> dict:
|
async def privileged(self) -> dict:
|
||||||
"""Check if you have privileged access.
|
"""Check if you have privileged access.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The STATUS section with an error if you have no
|
Returns:
|
||||||
privileged access, or success if you have privileged access.
|
The STATUS section with an error if you have no privileged access, or success if you have privileged access.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("privileged")
|
return await self.send_command("privileged")
|
||||||
|
|
||||||
async def pgaenable(self, n: int) -> dict:
|
async def pgaenable(self, n: int) -> dict:
|
||||||
"""Enable PGA n.
|
"""Enable PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA to enable.
|
Parameters:
|
||||||
|
n: The PGA to enable.
|
||||||
|
|
||||||
:return: A confirmation of enabling PGA n.
|
Returns:
|
||||||
|
A confirmation of enabling PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgaenable", parameters=n)
|
return await self.send_command("pgaenable", parameters=n)
|
||||||
|
|
||||||
async def pgadisable(self, n: int) -> dict:
|
async def pgadisable(self, n: int) -> dict:
|
||||||
"""Disable PGA n.
|
"""Disable PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA to disable.
|
Parameters:
|
||||||
|
n: The PGA to disable.
|
||||||
|
|
||||||
:return: A confirmation of disabling PGA n.
|
Returns:
|
||||||
|
A confirmation of disabling PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgadisable", parameters=n)
|
return await self.send_command("pgadisable", parameters=n)
|
||||||
|
|
||||||
async def pgaidentify(self, n: int) -> dict:
|
async def pgaidentify(self, n: int) -> dict:
|
||||||
"""Identify PGA n.
|
"""Identify PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA to identify.
|
Parameters:
|
||||||
|
n: The PGA to identify.
|
||||||
|
|
||||||
:return: A confirmation of identifying PGA n.
|
Returns:
|
||||||
|
A confirmation of identifying PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgaidentify", parameters=n)
|
return await self.send_command("pgaidentify", parameters=n)
|
||||||
|
|
||||||
async def devdetails(self) -> dict:
|
async def devdetails(self) -> dict:
|
||||||
"""Get data on all devices with their static details.
|
"""Get data on all devices with their static details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all devices with their static details.
|
Returns:
|
||||||
|
Data on all devices with their static details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devdetails")
|
return await self.send_command("devdetails")
|
||||||
|
|
||||||
async def restart(self) -> dict:
|
async def restart(self) -> dict:
|
||||||
"""Restart BMMiner using the API.
|
"""Restart BMMiner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: A reply informing of the restart.
|
Returns:
|
||||||
|
A reply informing of the restart.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("restart")
|
return await self.send_command("restart")
|
||||||
|
|
||||||
async def stats(self) -> dict:
|
async def stats(self) -> dict:
|
||||||
"""Get stats of each device/pool with more than 1 getwork.
|
"""Get stats of each device/pool with more than 1 getwork.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Stats of each device/pool with more than 1 getwork.
|
Returns:
|
||||||
|
Stats of each device/pool with more than 1 getwork.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("stats")
|
return await self.send_command("stats")
|
||||||
|
|
||||||
async def estats(self, old: bool = False) -> dict:
|
async def estats(self, old: bool = False) -> dict:
|
||||||
"""Get stats of each device/pool with more than 1 getwork,
|
"""Get stats of each device/pool with more than 1 getwork, ignoring zombie devices.
|
||||||
ignoring zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param old: Include zombie devices that became zombies less
|
Parameters:
|
||||||
than 'old' seconds ago.
|
old: Include zombie devices that became zombies less than 'old' seconds ago.
|
||||||
|
|
||||||
:return: Stats of each device/pool with more than 1 getwork,
|
Returns:
|
||||||
ignoring zombie devices.
|
Stats of each device/pool with more than 1 getwork, ignoring zombie devices.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if old:
|
if old:
|
||||||
return await self.send_command("estats", parameters=old)
|
return await self.send_command("estats", parameters=old)
|
||||||
@@ -266,92 +378,126 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def check(self, command: str) -> dict:
|
async def check(self, command: str) -> dict:
|
||||||
"""Check if the command command exists in BMMiner.
|
"""Check if the command command exists in BMMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param command: The command to check.
|
Parameters:
|
||||||
|
command: The command to check.
|
||||||
|
|
||||||
:return: Information about a command:
|
Returns:
|
||||||
Exists (Y/N) <- the command exists in this version
|
## Information about a command:
|
||||||
Access (Y/N) <- you have access to use the command
|
* Exists (Y/N) <- the command exists in this version
|
||||||
|
* Access (Y/N) <- you have access to use the command
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("check", parameters=command)
|
return await self.send_command("check", parameters=command)
|
||||||
|
|
||||||
async def failover_only(self, failover: bool) -> dict:
|
async def failover_only(self, failover: bool) -> dict:
|
||||||
"""Set failover-only.
|
"""Set failover-only.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
failover: What to set failover-only to.
|
||||||
|
|
||||||
:param failover: What to set failover-only to.
|
Returns:
|
||||||
|
Confirmation of setting failover-only.
|
||||||
:return: Confirmation of setting failover-only.
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("failover-only", parameters=failover)
|
return await self.send_command("failover-only", parameters=failover)
|
||||||
|
|
||||||
async def coin(self) -> dict:
|
async def coin(self) -> dict:
|
||||||
"""Get information on the current coin.
|
"""Get information on the current coin.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Information about the current coin being mined:
|
Returns:
|
||||||
Hash Method <- the hashing algorithm
|
## Information about the current coin being mined:
|
||||||
Current Block Time <- blocktime as a float, 0 means none
|
* Hash Method <- the hashing algorithm
|
||||||
Current Block Hash <- the hash of the current block, blank
|
* Current Block Time <- blocktime as a float, 0 means none
|
||||||
means none
|
* Current Block Hash <- the hash of the current block, blank means none
|
||||||
LP <- whether LP is in use on at least 1 pool
|
* LP <- whether LP is in use on at least 1 pool
|
||||||
Network Difficulty: the current network difficulty
|
* Network Difficulty: the current network difficulty
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("coin")
|
return await self.send_command("coin")
|
||||||
|
|
||||||
async def debug(self, setting: str) -> dict:
|
async def debug(self, setting: str) -> dict:
|
||||||
"""Set a debug setting.
|
"""Set a debug setting.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param setting: Which setting to switch to. Options are:
|
Parameters:
|
||||||
Silent,
|
setting: Which setting to switch to.
|
||||||
Quiet,
|
## Options are:
|
||||||
Verbose,
|
* Silent
|
||||||
Debug,
|
* Quiet
|
||||||
RPCProto,
|
* Verbose
|
||||||
PerDevice,
|
* Debug
|
||||||
WorkTime,
|
* RPCProto
|
||||||
Normal.
|
* PerDevice
|
||||||
|
* WorkTime
|
||||||
|
* Normal
|
||||||
|
|
||||||
:return: Data on which debug setting was enabled or disabled.
|
Returns:
|
||||||
|
Data on which debug setting was enabled or disabled.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("debug", parameters=setting)
|
return await self.send_command("debug", parameters=setting)
|
||||||
|
|
||||||
async def setconfig(self, name: str, n: int) -> dict:
|
async def setconfig(self, name: str, n: int) -> dict:
|
||||||
"""Set config of name to value n.
|
"""Set config of name to value n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param name: The name of the config setting to set. Options are:
|
Parameters:
|
||||||
queue,
|
name: The name of the config setting to set.
|
||||||
scantime,
|
## Options are:
|
||||||
expiry.
|
* queue
|
||||||
:param n: The value to set the 'name' setting to.
|
* scantime
|
||||||
|
* expiry
|
||||||
|
n: The value to set the 'name' setting to.
|
||||||
|
|
||||||
:return: The results of setting config of name to n.
|
Returns:
|
||||||
|
The results of setting config of name to n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("setconfig", parameters=f"{name},{n}")
|
return await self.send_command("setconfig", parameters=f"{name},{n}")
|
||||||
|
|
||||||
async def usbstats(self) -> dict:
|
async def usbstats(self) -> dict:
|
||||||
"""Get stats of all USB devices except ztex.
|
"""Get stats of all USB devices except ztex.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The stats of all USB devices except ztex.
|
Returns:
|
||||||
|
The stats of all USB devices except ztex.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("usbstats")
|
return await self.send_command("usbstats")
|
||||||
|
|
||||||
async def pgaset(self, n: int, opt: str, val: int = None) -> dict:
|
async def pgaset(self, n: int, opt: str, val: int = None) -> dict:
|
||||||
"""Set PGA option opt to val on PGA n.
|
"""Set PGA option opt to val on PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
```
|
||||||
MMQ -
|
MMQ -
|
||||||
opt: clock
|
opt: clock
|
||||||
val: 160 - 230 (multiple of 2)
|
val: 160 - 230 (multiple of 2)
|
||||||
CMR -
|
CMR -
|
||||||
opt: clock
|
opt: clock
|
||||||
val: 100 - 220
|
val: 100 - 220
|
||||||
|
```
|
||||||
|
|
||||||
:param n: The PGA to set the options on.
|
Parameters:
|
||||||
:param opt: The option to set. Setting this to 'help'
|
n: The PGA to set the options on.
|
||||||
returns a help message.
|
opt: The option to set. Setting this to 'help' returns a help message.
|
||||||
:param val: The value to set the option to.
|
val: The value to set the option to.
|
||||||
|
|
||||||
:return: Confirmation of setting PGA n with opt[,val].
|
Returns:
|
||||||
|
Confirmation of setting PGA n with opt[,val].
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if val:
|
if val:
|
||||||
return await self.send_command("pgaset", parameters=f"{n},{opt},{val}")
|
return await self.send_command("pgaset", parameters=f"{n},{opt},{val}")
|
||||||
@@ -360,75 +506,108 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def zero(self, which: str, summary: bool) -> dict:
|
async def zero(self, which: str, summary: bool) -> dict:
|
||||||
"""Zero a device.
|
"""Zero a device.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param which: Which device to zero.
|
Parameters:
|
||||||
Setting this to 'all' zeros all devices.
|
which: Which device to zero. Setting this to 'all' zeros all devices. Setting this to 'bestshare' zeros only the bestshare values for each pool and global.
|
||||||
Setting this to 'bestshare' zeros only the bestshare values
|
summary: Whether or not to show a full summary.
|
||||||
for each pool and global.
|
|
||||||
:param summary: Whether or not to show a full summary.
|
|
||||||
|
|
||||||
|
|
||||||
:return: the STATUS section with info on the zero and optional
|
Returns:
|
||||||
summary.
|
the STATUS section with info on the zero and optional summary.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("zero", parameters=f"{which},{summary}")
|
return await self.send_command("zero", parameters=f"{which},{summary}")
|
||||||
|
|
||||||
async def hotplug(self, n: int) -> dict:
|
async def hotplug(self, n: int) -> dict:
|
||||||
"""Enable hotplug.
|
"""Enable hotplug.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device number to set hotplug on.
|
Parameters:
|
||||||
|
n: The device number to set hotplug on.
|
||||||
|
|
||||||
:return: Information on hotplug status.
|
Returns:
|
||||||
|
Information on hotplug status.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("hotplug", parameters=n)
|
return await self.send_command("hotplug", parameters=n)
|
||||||
|
|
||||||
async def asc(self, n: int) -> dict:
|
async def asc(self, n: int) -> dict:
|
||||||
"""Get data for ASC device n.
|
"""Get data for ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to get data for.
|
Parameters:
|
||||||
|
n: The device to get data for.
|
||||||
|
|
||||||
:return: The data for ASC device n.
|
Returns:
|
||||||
|
The data for ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("asc", parameters=n)
|
return await self.send_command("asc", parameters=n)
|
||||||
|
|
||||||
async def ascenable(self, n: int) -> dict:
|
async def ascenable(self, n: int) -> dict:
|
||||||
"""Enable ASC device n.
|
"""Enable ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to enable.
|
Parameters:
|
||||||
|
n: The device to enable.
|
||||||
|
|
||||||
:return: Confirmation of enabling ASC device n.
|
Returns:
|
||||||
|
Confirmation of enabling ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("ascenable", parameters=n)
|
return await self.send_command("ascenable", parameters=n)
|
||||||
|
|
||||||
async def ascdisable(self, n: int) -> dict:
|
async def ascdisable(self, n: int) -> dict:
|
||||||
"""Disable ASC device n.
|
"""Disable ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to disable.
|
Parameters:
|
||||||
|
n: The device to disable.
|
||||||
|
|
||||||
:return: Confirmation of disabling ASC device n.
|
Returns:
|
||||||
|
Confirmation of disabling ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("ascdisable", parameters=n)
|
return await self.send_command("ascdisable", parameters=n)
|
||||||
|
|
||||||
async def ascidentify(self, n: int) -> dict:
|
async def ascidentify(self, n: int) -> dict:
|
||||||
"""Identify ASC device n.
|
"""Identify ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to identify.
|
Parameters:
|
||||||
|
n: The device to identify.
|
||||||
|
|
||||||
:return: Confirmation of identifying ASC device n.
|
Returns:
|
||||||
|
Confirmation of identifying ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("ascidentify", parameters=n)
|
return await self.send_command("ascidentify", parameters=n)
|
||||||
|
|
||||||
async def asccount(self) -> dict:
|
async def asccount(self) -> dict:
|
||||||
"""Get data on the number of ASC devices and their info.
|
"""Get data on the number of ASC devices and their info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all ASC devices.
|
Returns:
|
||||||
|
Data on all ASC devices.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("asccount")
|
return await self.send_command("asccount")
|
||||||
|
|
||||||
async def ascset(self, n: int, opt: str, val: int = None) -> dict:
|
async def ascset(self, n: int, opt: str, val: int = None) -> dict:
|
||||||
"""Set ASC n option opt to value val.
|
"""Set ASC n option opt to value val.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Sets an option on the ASC n to a value. Allowed options are:
|
Sets an option on the ASC n to a value. Allowed options are:
|
||||||
|
```
|
||||||
AVA+BTB -
|
AVA+BTB -
|
||||||
opt: freq
|
opt: freq
|
||||||
val: 256 - 1024 (chip frequency)
|
val: 256 - 1024 (chip frequency)
|
||||||
@@ -462,14 +641,16 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
opt: clock
|
opt: clock
|
||||||
val: 0 - 15
|
val: 0 - 15
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The ASC to set the options on.
|
||||||
|
opt: The option to set. Setting this to 'help' returns a help message.
|
||||||
|
val: The value to set the option to.
|
||||||
|
|
||||||
:param n: The ASC to set the options on.
|
Returns:
|
||||||
:param opt: The option to set. Setting this to 'help' returns a
|
Confirmation of setting option opt to value val.
|
||||||
help message.
|
</details>
|
||||||
:param val: The value to set the option to.
|
|
||||||
|
|
||||||
:return: Confirmation of setting option opt to value val.
|
|
||||||
"""
|
"""
|
||||||
if val:
|
if val:
|
||||||
return await self.send_command("ascset", parameters=f"{n},{opt},{val}")
|
return await self.send_command("ascset", parameters=f"{n},{opt},{val}")
|
||||||
@@ -478,14 +659,22 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def lcd(self) -> dict:
|
async def lcd(self) -> dict:
|
||||||
"""Get a general all-in-one status summary of the miner.
|
"""Get a general all-in-one status summary of the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: An all-in-one status summary of the miner.
|
Returns:
|
||||||
|
An all-in-one status summary of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("lcd")
|
return await self.send_command("lcd")
|
||||||
|
|
||||||
async def lockstats(self) -> dict:
|
async def lockstats(self) -> dict:
|
||||||
"""Write lockstats to STDERR.
|
"""Write lockstats to STDERR.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The result of writing the lock stats to STDERR.
|
Returns:
|
||||||
|
The result of writing the lock stats to STDERR.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("lockstats")
|
return await self.send_command("lockstats")
|
||||||
|
|||||||
@@ -6,60 +6,80 @@ class BOSMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
Each method corresponds to an API command in BOSMiner.
|
Each method corresponds to an API command in BOSMiner.
|
||||||
|
|
||||||
BOSMiner API documentation:
|
[BOSMiner API documentation](https://docs.braiins.com/os/plus-en/Development/1_api.html)
|
||||||
https://docs.braiins.com/os/plus-en/Development/1_api.html
|
|
||||||
|
|
||||||
This class abstracts use of the BOSMiner API, as well as the
|
This class abstracts use of the BOSMiner API, as well as the
|
||||||
methods for sending commands to it. The self.send_command()
|
methods for sending commands to it. The `self.send_command()`
|
||||||
function handles sending a command to the miner asynchronously, and
|
function handles sending a command to the miner asynchronously, and
|
||||||
as such is the base for many of the functions in this class, which
|
as such is the base for many of the functions in this class, which
|
||||||
rely on it to send the command for them.
|
rely on it to send the command for them.
|
||||||
|
|
||||||
:param ip: The IP of the miner to reference the API on.
|
Parameters:
|
||||||
:param port: The port to reference the API on. Default is 4028.
|
ip: The IP of the miner to reference the API on.
|
||||||
|
port: The port to reference the API on. Default is 4028.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip, port=4028):
|
def __init__(self, ip: str, port: int = 4028):
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port)
|
||||||
|
|
||||||
async def asccount(self) -> dict:
|
async def asccount(self) -> dict:
|
||||||
"""Get data on the number of ASC devices and their info.
|
"""Get data on the number of ASC devices and their info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all ASC devices.
|
Returns:
|
||||||
|
Data on all ASC devices.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("asccount")
|
return await self.send_command("asccount")
|
||||||
|
|
||||||
async def asc(self, n: int) -> dict:
|
async def asc(self, n: int) -> dict:
|
||||||
"""Get data for ASC device n.
|
"""Get data for ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to get data for.
|
Parameters:
|
||||||
|
n: The device to get data for.
|
||||||
|
|
||||||
:return: The data for ASC device n.
|
Returns:
|
||||||
|
The data for ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("asc", parameters=n)
|
return await self.send_command("asc", parameters=n)
|
||||||
|
|
||||||
async def devdetails(self) -> dict:
|
async def devdetails(self) -> dict:
|
||||||
"""Get data on all devices with their static details.
|
"""Get data on all devices with their static details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all devices with their static details.
|
Returns:
|
||||||
|
Data on all devices with their static details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devdetails")
|
return await self.send_command("devdetails")
|
||||||
|
|
||||||
async def devs(self) -> dict:
|
async def devs(self) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details.
|
"""Get data on each PGA/ASC with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devs")
|
return await self.send_command("devs")
|
||||||
|
|
||||||
async def edevs(self, old: bool = False) -> dict:
|
async def edevs(self, old: bool = False) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details, ignoring
|
"""Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
|
||||||
blacklisted and zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param old: Include zombie devices that became zombies less
|
Parameters:
|
||||||
than 'old' seconds ago
|
old: Include zombie devices that became zombies less than 'old' seconds ago
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if old:
|
if old:
|
||||||
return await self.send_command("edevs", parameters="old")
|
return await self.send_command("edevs", parameters="old")
|
||||||
@@ -69,40 +89,62 @@ class BOSMinerAPI(BaseMinerAPI):
|
|||||||
async def pools(self) -> dict:
|
async def pools(self) -> dict:
|
||||||
"""Get pool information.
|
"""Get pool information.
|
||||||
|
|
||||||
:return: Miner pool information.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner pool information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pools")
|
return await self.send_command("pools")
|
||||||
|
|
||||||
async def summary(self) -> dict:
|
async def summary(self) -> dict:
|
||||||
"""Get the status summary of the miner.
|
"""Get the status summary of the miner.
|
||||||
|
|
||||||
:return: The status summary of the miner.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The status summary of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("summary")
|
return await self.send_command("summary")
|
||||||
|
|
||||||
async def stats(self) -> dict:
|
async def stats(self) -> dict:
|
||||||
"""Get stats of each device/pool with more than 1 getwork.
|
"""Get stats of each device/pool with more than 1 getwork.
|
||||||
|
|
||||||
:return: Stats of each device/pool with more than 1 getwork.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Stats of each device/pool with more than 1 getwork.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("stats")
|
return await self.send_command("stats")
|
||||||
|
|
||||||
async def version(self) -> dict:
|
async def version(self) -> dict:
|
||||||
"""Get miner version info.
|
"""Get miner version info.
|
||||||
|
|
||||||
:return: Miner version information.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner version information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("version")
|
return await self.send_command("version")
|
||||||
|
|
||||||
async def estats(self, old: bool = False) -> dict:
|
async def estats(self, old: bool = False) -> dict:
|
||||||
"""Get stats of each device/pool with more than 1 getwork,
|
"""Get stats of each device/pool with more than 1 getwork, ignoring zombie devices.
|
||||||
ignoring zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param old: Include zombie devices that became zombies less
|
Parameters:
|
||||||
than 'old' seconds ago.
|
old: Include zombie devices that became zombies less than 'old' seconds ago.
|
||||||
|
|
||||||
:return: Stats of each device/pool with more than 1 getwork,
|
Returns:
|
||||||
ignoring zombie devices.
|
Stats of each device/pool with more than 1 getwork, ignoring zombie devices.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if old:
|
if old:
|
||||||
return await self.send_command("estats", parameters=old)
|
return await self.send_command("estats", parameters=old)
|
||||||
@@ -111,98 +153,109 @@ class BOSMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def check(self, command: str) -> dict:
|
async def check(self, command: str) -> dict:
|
||||||
"""Check if the command command exists in BOSMiner.
|
"""Check if the command command exists in BOSMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param command: The command to check.
|
Parameters:
|
||||||
|
command: The command to check.
|
||||||
|
|
||||||
:return: Information about a command:
|
Returns:
|
||||||
Exists (Y/N) <- the command exists in this version
|
## Information about a command:
|
||||||
Access (Y/N) <- you have access to use the command
|
* Exists (Y/N) <- the command exists in this version
|
||||||
|
* Access (Y/N) <- you have access to use the command
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("check", parameters=command)
|
return await self.send_command("check", parameters=command)
|
||||||
|
|
||||||
async def coin(self) -> dict:
|
async def coin(self) -> dict:
|
||||||
"""Get information on the current coin.
|
"""Get information on the current coin.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Information about the current coin being mined:
|
Returns:
|
||||||
Hash Method <- the hashing algorithm
|
## Information about the current coin being mined:
|
||||||
Current Block Time <- blocktime as a float, 0 means none
|
* Hash Method <- the hashing algorithm
|
||||||
Current Block Hash <- the hash of the current block, blank
|
* Current Block Time <- blocktime as a float, 0 means none
|
||||||
means none
|
* Current Block Hash <- the hash of the current block, blank means none
|
||||||
LP <- whether LP is in use on at least 1 pool
|
* LP <- whether LP is in use on at least 1 pool
|
||||||
Network Difficulty: the current network difficulty
|
* Network Difficulty: the current network difficulty
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("coin")
|
return await self.send_command("coin")
|
||||||
|
|
||||||
async def lcd(self) -> dict:
|
async def lcd(self) -> dict:
|
||||||
"""Get a general all-in-one status summary of the miner.
|
"""Get a general all-in-one status summary of the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: An all-in-one status summary of the miner.
|
Returns:
|
||||||
|
An all-in-one status summary of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("lcd")
|
return await self.send_command("lcd")
|
||||||
|
|
||||||
async def switchpool(self, n: int) -> dict:
|
|
||||||
# BOS has not implemented this yet, they will in the future
|
|
||||||
raise NotImplementedError
|
|
||||||
# return await self.send_command("switchpool", parameters=n)
|
|
||||||
|
|
||||||
async def enablepool(self, n: int) -> dict:
|
|
||||||
# BOS has not implemented this yet, they will in the future
|
|
||||||
raise NotImplementedError
|
|
||||||
# return await self.send_command("enablepool", parameters=n)
|
|
||||||
|
|
||||||
async def disablepool(self, n: int) -> dict:
|
|
||||||
# BOS has not implemented this yet, they will in the future
|
|
||||||
raise NotImplementedError
|
|
||||||
# return await self.send_command("disablepool", parameters=n)
|
|
||||||
|
|
||||||
async def addpool(self, url: str, username: str, password: str) -> dict:
|
|
||||||
# BOS has not implemented this yet, they will in the future
|
|
||||||
raise NotImplementedError
|
|
||||||
# return await self.send_command("addpool", parameters=f"{url},{username},{password}")
|
|
||||||
|
|
||||||
async def removepool(self, n: int) -> dict:
|
|
||||||
# BOS has not implemented this yet, they will in the future
|
|
||||||
raise NotImplementedError
|
|
||||||
# return await self.send_command("removepool", parameters=n)
|
|
||||||
|
|
||||||
async def fans(self) -> dict:
|
async def fans(self) -> dict:
|
||||||
"""Get fan data.
|
"""Get fan data.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on the fans of the miner.
|
Returns:
|
||||||
|
Data on the fans of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("fans")
|
return await self.send_command("fans")
|
||||||
|
|
||||||
async def tempctrl(self) -> dict:
|
async def tempctrl(self) -> dict:
|
||||||
"""Get temperature control data.
|
"""Get temperature control data.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data about the temp control settings of the miner.
|
Returns:
|
||||||
|
Data about the temp control settings of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("tempctrl")
|
return await self.send_command("tempctrl")
|
||||||
|
|
||||||
async def temps(self) -> dict:
|
async def temps(self) -> dict:
|
||||||
"""Get temperature data.
|
"""Get temperature data.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on the temps of the miner.
|
Returns:
|
||||||
|
Data on the temps of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("temps")
|
return await self.send_command("temps")
|
||||||
|
|
||||||
async def tunerstatus(self) -> dict:
|
async def tunerstatus(self) -> dict:
|
||||||
"""Get tuner status data
|
"""Get tuner status data
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on the status of autotuning.
|
Returns:
|
||||||
|
Data on the status of autotuning.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("tunerstatus")
|
return await self.send_command("tunerstatus")
|
||||||
|
|
||||||
async def pause(self) -> dict:
|
async def pause(self) -> dict:
|
||||||
"""Pause mining.
|
"""Pause mining.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Confirmation of pausing mining.
|
Returns:
|
||||||
|
Confirmation of pausing mining.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pause")
|
return await self.send_command("pause")
|
||||||
|
|
||||||
async def resume(self) -> dict:
|
async def resume(self) -> dict:
|
||||||
"""Resume mining.
|
"""Resume mining.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Confirmation of resuming mining.
|
Returns:
|
||||||
|
Confirmation of resuming mining.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("resume")
|
return await self.send_command("resume")
|
||||||
|
|||||||
@@ -29,9 +29,12 @@ def _crypt(word: str, salt: str) -> str:
|
|||||||
'\s*\$(\d+)\$([\w\./]*)\$'. If this format is not used, a
|
'\s*\$(\d+)\$([\w\./]*)\$'. If this format is not used, a
|
||||||
ValueError is raised.
|
ValueError is raised.
|
||||||
|
|
||||||
:param word: The word to be encrypted.
|
Parameters:
|
||||||
:param salt: The salt to encrypt the word.
|
word: The word to be encrypted.
|
||||||
:return: An MD5 hash of the word with the salt.
|
salt: The salt to encrypt the word.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An MD5 hash of the word with the salt.
|
||||||
"""
|
"""
|
||||||
# compile a standard format for the salt
|
# compile a standard format for the salt
|
||||||
standard_salt = re.compile("\s*\$(\d+)\$([\w\./]*)\$")
|
standard_salt = re.compile("\s*\$(\d+)\$([\w\./]*)\$")
|
||||||
@@ -50,11 +53,11 @@ def _crypt(word: str, salt: str) -> str:
|
|||||||
def _add_to_16(string: str) -> bytes:
|
def _add_to_16(string: str) -> bytes:
|
||||||
"""Add null bytes to a string until the length is a multiple 16
|
"""Add null bytes to a string until the length is a multiple 16
|
||||||
|
|
||||||
:param string: The string to lengthen to a multiple of 16 and
|
Parameters:
|
||||||
encode.
|
string: The string to lengthen to a multiple of 16 and encode.
|
||||||
|
|
||||||
:return: The input string as bytes with a multiple of 16 as the
|
Returns:
|
||||||
length.
|
The input string as bytes with a multiple of 16 as the length.
|
||||||
"""
|
"""
|
||||||
while len(string) % 16 != 0:
|
while len(string) % 16 != 0:
|
||||||
string += "\0"
|
string += "\0"
|
||||||
@@ -67,10 +70,12 @@ def parse_btminer_priviledge_data(token_data: dict, data: dict):
|
|||||||
Parses data from the BTMiner privileged API using the the token
|
Parses data from the BTMiner privileged API using the the token
|
||||||
from the API in an AES format.
|
from the API in an AES format.
|
||||||
|
|
||||||
:param token_data: The token information from self.get_token().
|
Parameters:
|
||||||
:param data: The data to parse, returned from the API.
|
token_data: The token information from self.get_token().
|
||||||
|
data: The data to parse, returned from the API.
|
||||||
|
|
||||||
:return: A decoded dict version of the privileged command output.
|
Returns:
|
||||||
|
A decoded dict version of the privileged command output.
|
||||||
"""
|
"""
|
||||||
# get the encoded data from the dict
|
# get the encoded data from the dict
|
||||||
enc_data = data["enc"]
|
enc_data = data["enc"]
|
||||||
@@ -97,10 +102,12 @@ def create_privileged_cmd(token_data: dict, command: dict) -> bytes:
|
|||||||
command as a dict of {'command': cmd}, with cmd being any command
|
command as a dict of {'command': cmd}, with cmd being any command
|
||||||
that the miner API accepts.
|
that the miner API accepts.
|
||||||
|
|
||||||
:param token_data: The token information from self.get_token().
|
Parameters:
|
||||||
:param command: The command to turn into a privileged command.
|
token_data: The token information from self.get_token().
|
||||||
|
command: The command to turn into a privileged command.
|
||||||
|
|
||||||
:return: The encrypted privileged command to be sent to the miner.
|
Returns:
|
||||||
|
The encrypted privileged command to be sent to the miner.
|
||||||
"""
|
"""
|
||||||
# add token to command
|
# add token to command
|
||||||
command["token"] = token_data["host_sign"]
|
command["token"] = token_data["host_sign"]
|
||||||
@@ -132,7 +139,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
Each method corresponds to an API command in BMMiner.
|
Each method corresponds to an API command in BMMiner.
|
||||||
|
|
||||||
This class abstracts use of the BTMiner API, as well as the
|
This class abstracts use of the BTMiner API, as well as the
|
||||||
methods for sending commands to it. The self.send_command()
|
methods for sending commands to it. The `self.send_command()`
|
||||||
function handles sending a command to the miner asynchronously, and
|
function handles sending a command to the miner asynchronously, and
|
||||||
as such is the base for many of the functions in this class, which
|
as such is the base for many of the functions in this class, which
|
||||||
rely on it to send the command for them.
|
rely on it to send the command for them.
|
||||||
@@ -148,12 +155,13 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
this automatically for you and will decode the output to look like
|
this automatically for you and will decode the output to look like
|
||||||
a normal output from a miner API.
|
a normal output from a miner API.
|
||||||
|
|
||||||
:param ip: The IP of the miner to reference the API on.
|
Parameters:
|
||||||
:param port: The port to reference the API on. Default is 4028.
|
ip: The IP of the miner to reference the API on.
|
||||||
:param pwd: The admin password of the miner. Default is admin.
|
port: The port to reference the API on. Default is 4028.
|
||||||
|
pwd: The admin password of the miner. Default is admin.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip, port=4028, pwd: str = WHATSMINER_PWD):
|
def __init__(self, ip: str, port: int = 4028, pwd: str = WHATSMINER_PWD):
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port)
|
||||||
self.admin_pwd = pwd
|
self.admin_pwd = pwd
|
||||||
self.current_token = None
|
self.current_token = None
|
||||||
@@ -165,19 +173,6 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
ignore_errors: bool = False,
|
ignore_errors: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Send a command to the miner API.
|
|
||||||
|
|
||||||
Send a command using an asynchronous connection, load the data,
|
|
||||||
parse encoded data if needed, and return the result.
|
|
||||||
|
|
||||||
:param command: The command to send to the miner.
|
|
||||||
:param parameters: Parameters to pass to the command.
|
|
||||||
:param ignore_errors: Ignore the E (Error) status code from the
|
|
||||||
API.
|
|
||||||
|
|
||||||
:return: The data received from the API after sending the
|
|
||||||
command.
|
|
||||||
"""
|
|
||||||
# check if command is a string
|
# check if command is a string
|
||||||
# if its bytes its encoded and needs to be sent raw
|
# if its bytes its encoded and needs to be sent raw
|
||||||
if isinstance(command, str):
|
if isinstance(command, str):
|
||||||
@@ -232,11 +227,14 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
# return the parsed json as a dict
|
# return the parsed json as a dict
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def get_token(self):
|
async def get_token(self) -> dict:
|
||||||
"""Gets token information from the API.
|
"""Gets token information from the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: An encoded token and md5 password, which are used
|
Returns:
|
||||||
for the privileged API.
|
An encoded token and md5 password, which are used for the privileged API.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
# get the token
|
# get the token
|
||||||
data = await self.send_command("get_token")
|
data = await self.send_command("get_token")
|
||||||
@@ -278,22 +276,28 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
pool_3: str = None,
|
pool_3: str = None,
|
||||||
worker_3: str = None,
|
worker_3: str = None,
|
||||||
passwd_3: str = None,
|
passwd_3: str = None,
|
||||||
):
|
) -> dict:
|
||||||
"""Update the pools of the miner using the API.
|
"""Update the pools of the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Update the pools of the miner using the API, only works after
|
Update the pools of the miner using the API, only works after
|
||||||
changing the password of the miner using the Whatsminer tool.
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:param pool_1: The URL to update pool 1 to.
|
Parameters:
|
||||||
:param worker_1: The worker name for pool 1 to update to.
|
pool_1: The URL to update pool 1 to.
|
||||||
:param passwd_1: The password for pool 1 to update to.
|
worker_1: The worker name for pool 1 to update to.
|
||||||
:param pool_2: The URL to update pool 2 to.
|
passwd_1: The password for pool 1 to update to.
|
||||||
:param worker_2: The worker name for pool 2 to update to.
|
pool_2: The URL to update pool 2 to.
|
||||||
:param passwd_2: The password for pool 2 to update to.
|
worker_2: The worker name for pool 2 to update to.
|
||||||
:param pool_3: The URL to update pool 3 to.
|
passwd_2: The password for pool 2 to update to.
|
||||||
:param worker_3: The worker name for pool 3 to update to.
|
pool_3: The URL to update pool 3 to.
|
||||||
:param passwd_3: The password for pool 3 to update to.
|
worker_3: The worker name for pool 3 to update to.
|
||||||
:return: A dict from the API to confirm the pools were updated.
|
passwd_3: The password for pool 3 to update to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dict from the API to confirm the pools were updated.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
# get the token and password from the miner
|
# get the token and password from the miner
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
@@ -336,27 +340,36 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
# send the command
|
# send the command
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def restart(self):
|
async def restart(self) -> dict:
|
||||||
"""Restart BTMiner using the API.
|
"""Restart BTMiner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Restart BTMiner using the API, only works after changing
|
Restart BTMiner using the API, only works after changing
|
||||||
the password of the miner using the Whatsminer tool.
|
the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the restart.
|
Returns:
|
||||||
|
A reply informing of the restart.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "restart_btminer"}
|
command = {"cmd": "restart_btminer"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def power_off(self, respbefore: bool = True):
|
async def power_off(self, respbefore: bool = True) -> dict:
|
||||||
"""Power off the miner using the API.
|
"""Power off the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Power off the miner using the API, only works after changing
|
Power off the miner using the API, only works after changing
|
||||||
the password of the miner using the Whatsminer tool.
|
the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:param respbefore: Whether to respond before powering off.
|
Parameters:
|
||||||
:return: A reply informing of the status of powering off.
|
respbefore: Whether to respond before powering off.
|
||||||
|
Returns:
|
||||||
|
A reply informing of the status of powering off.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if respbefore:
|
if respbefore:
|
||||||
command = {"cmd": "power_off", "respbefore": "true"}
|
command = {"cmd": "power_off", "respbefore": "true"}
|
||||||
@@ -366,26 +379,36 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def power_on(self):
|
async def power_on(self) -> dict:
|
||||||
"""Power on the miner using the API.
|
"""Power on the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Power on the miner using the API, only works after changing
|
Power on the miner using the API, only works after changing
|
||||||
the password of the miner using the Whatsminer tool.
|
the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of powering on.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of powering on.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "power_on"}
|
command = {"cmd": "power_on"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def reset_led(self):
|
async def reset_led(self) -> dict:
|
||||||
"""Reset the LED on the miner using the API.
|
"""Reset the LED on the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Reset the LED on the miner using the API, only works after
|
Reset the LED on the miner using the API, only works after
|
||||||
changing the password of the miner using the Whatsminer tool.
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of resetting the LED.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of resetting the LED.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "set_led", "param": "auto"}
|
command = {"cmd": "set_led", "param": "auto"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
@@ -398,17 +421,23 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
period: int = 2000,
|
period: int = 2000,
|
||||||
duration: int = 1000,
|
duration: int = 1000,
|
||||||
start: int = 0,
|
start: int = 0,
|
||||||
):
|
) -> dict:
|
||||||
"""Set the LED on the miner using the API.
|
"""Set the LED on the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Set the LED on the miner using the API, only works after
|
Set the LED on the miner using the API, only works after
|
||||||
changing the password of the miner using the Whatsminer tool.
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:param color: The LED color to set, either 'red' or 'green'.
|
Parameters:
|
||||||
:param period: The flash cycle in ms.
|
color: The LED color to set, either 'red' or 'green'.
|
||||||
:param duration: LED on time in the cycle in ms.
|
period: The flash cycle in ms.
|
||||||
:param start: LED on time offset in the cycle in ms.
|
duration: LED on time in the cycle in ms.
|
||||||
:return: A reply informing of the status of setting the LED.
|
start: LED on time offset in the cycle in ms.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting the LED.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {
|
command = {
|
||||||
"cmd": "set_led",
|
"cmd": "set_led",
|
||||||
@@ -421,14 +450,18 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def set_low_power(self):
|
async def set_low_power(self) -> dict:
|
||||||
"""Set low power mode on the miner using the API.
|
"""Set low power mode on the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Set low power mode on the miner using the API, only works after
|
Set low power mode on the miner using the API, only works after
|
||||||
changing the password of the miner using the Whatsminer tool.
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of setting low power
|
Returns:
|
||||||
mode.
|
|
||||||
|
A reply informing of the status of setting low power mode.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "set_low_power"}
|
command = {"cmd": "set_low_power"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
@@ -436,46 +469,62 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def update_firmware(self): # noqa - static
|
async def update_firmware(self): # noqa - static
|
||||||
|
"""Not implemented."""
|
||||||
# to be determined if this will be added later
|
# to be determined if this will be added later
|
||||||
# requires a file stream in bytes
|
# requires a file stream in bytes
|
||||||
return NotImplementedError
|
return NotImplementedError
|
||||||
|
|
||||||
async def reboot(self):
|
async def reboot(self) -> dict:
|
||||||
"""Reboot the miner using the API.
|
"""Reboot the miner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: A reply informing of the status of the reboot.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of the reboot.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "reboot"}
|
command = {"cmd": "reboot"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def factory_reset(self):
|
async def factory_reset(self) -> dict:
|
||||||
"""Reset the miner to factory defaults.
|
"""Reset the miner to factory defaults.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: A reply informing of the status of the reset.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of the reset.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "factory_reset"}
|
command = {"cmd": "factory_reset"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def update_pwd(self, old_pwd: str, new_pwd: str):
|
async def update_pwd(self, old_pwd: str, new_pwd: str) -> dict:
|
||||||
"""Update the admin user's password.
|
"""Update the admin user's password.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Update the admin user's password, only works after changing the
|
Update the admin user's password, only works after changing the
|
||||||
password of the miner using the Whatsminer tool. New password
|
password of the miner using the Whatsminer tool. New password
|
||||||
has a max length of 8 bytes, using letters, numbers, and
|
has a max length of 8 bytes, using letters, numbers, and
|
||||||
underscores.
|
underscores.
|
||||||
|
|
||||||
:param old_pwd: The old admin password.
|
Parameters:
|
||||||
:param new_pwd: The new password to set.
|
old_pwd: The old admin password.
|
||||||
:return: A reply informing of the status of setting the
|
new_pwd: The new password to set.
|
||||||
password.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting the password.
|
||||||
"""
|
"""
|
||||||
# check if password length is greater than 8 bytes
|
# check if password length is greater than 8 bytes
|
||||||
if len(new_pwd.encode("utf-8")) > 8:
|
if len(new_pwd.encode("utf-8")) > 8:
|
||||||
return APIError(
|
raise APIError(
|
||||||
f"New password too long, the max length is 8. "
|
f"New password too long, the max length is 8. "
|
||||||
f"Password size: {len(new_pwd.encode('utf-8'))}"
|
f"Password size: {len(new_pwd.encode('utf-8'))}"
|
||||||
)
|
)
|
||||||
@@ -484,19 +533,25 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def set_target_freq(self, percent: int):
|
async def set_target_freq(self, percent: int) -> dict:
|
||||||
"""Update the target frequency.
|
"""Update the target frequency.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Update the target frequency, only works after changing the
|
Update the target frequency, only works after changing the
|
||||||
password of the miner using the Whatsminer tool. The new
|
password of the miner using the Whatsminer tool. The new
|
||||||
frequency must be between -10% and 100%.
|
frequency must be between -10% and 100%.
|
||||||
|
|
||||||
:param percent: The frequency % to set.
|
Parameters:
|
||||||
:return: A reply informing of the status of setting the
|
percent: The frequency % to set.
|
||||||
frequency.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting the frequency.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if not -10 < percent < 100:
|
if not -10 < percent < 100:
|
||||||
return APIError(
|
raise APIError(
|
||||||
f"Frequency % is outside of the allowed "
|
f"Frequency % is outside of the allowed "
|
||||||
f"range. Please set a % between -10 and "
|
f"range. Please set a % between -10 and "
|
||||||
f"100"
|
f"100"
|
||||||
@@ -506,88 +561,122 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def enable_fast_boot(self):
|
async def enable_fast_boot(self) -> dict:
|
||||||
"""Turn on fast boot.
|
"""Turn on fast boot.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Turn on fast boot, only works after changing the password of
|
Turn on fast boot, only works after changing the password of
|
||||||
the miner using the Whatsminer tool.
|
the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of enabling fast boot.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of enabling fast boot.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "enable_btminer_fast_boot"}
|
command = {"cmd": "enable_btminer_fast_boot"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def disable_fast_boot(self):
|
async def disable_fast_boot(self) -> dict:
|
||||||
"""Turn off fast boot.
|
"""Turn off fast boot.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Turn off fast boot, only works after changing the password of
|
Turn off fast boot, only works after changing the password of
|
||||||
the miner using the Whatsminer tool.
|
the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of disabling fast boot.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of disabling fast boot.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "disable_btminer_fast_boot"}
|
command = {"cmd": "disable_btminer_fast_boot"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def enable_web_pools(self):
|
async def enable_web_pools(self) -> dict:
|
||||||
"""Turn on web pool updates.
|
"""Turn on web pool updates.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Turn on web pool updates, only works after changing the
|
Turn on web pool updates, only works after changing the
|
||||||
password of the miner using the Whatsminer tool.
|
password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of enabling web pools.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of enabling web pools.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "enable_web_pools"}
|
command = {"cmd": "enable_web_pools"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def disable_web_pools(self):
|
async def disable_web_pools(self) -> dict:
|
||||||
"""Turn off web pool updates.
|
"""Turn off web pool updates.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Turn off web pool updates, only works after changing the
|
Turn off web pool updates, only works after changing the
|
||||||
password of the miner using the Whatsminer tool.
|
password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:return: A reply informing of the status of disabling web
|
Returns:
|
||||||
pools.
|
|
||||||
|
A reply informing of the status of disabling web pools.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "disable_web_pools"}
|
command = {"cmd": "disable_web_pools"}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def set_hostname(self, hostname: str):
|
async def set_hostname(self, hostname: str) -> dict:
|
||||||
"""Set the hostname of the miner.
|
"""Set the hostname of the miner.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Set the hostname of the miner, only works after changing the
|
Set the hostname of the miner, only works after changing the
|
||||||
password of the miner using the Whatsminer tool.
|
password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
hostname: The new hostname to use.
|
||||||
|
Returns:
|
||||||
|
|
||||||
:param hostname: The new hostname to use.
|
A reply informing of the status of setting the hostname.
|
||||||
:return: A reply informing of the status of setting the
|
</details>
|
||||||
hostname.
|
|
||||||
"""
|
"""
|
||||||
command = {"cmd": "set_hostname", "hostname": hostname}
|
command = {"cmd": "set_hostname", "hostname": hostname}
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def set_power_pct(self, percent: int):
|
async def set_power_pct(self, percent: int) -> dict:
|
||||||
"""Set the power percentage of the miner.
|
"""Set the power percentage of the miner.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Set the power percentage of the miner, only works after changing
|
Set the power percentage of the miner, only works after changing
|
||||||
the password of the miner using the Whatsminer tool.
|
the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:param percent: The power percentage to set.
|
Parameters:
|
||||||
:return: A reply informing of the status of setting the
|
percent: The power percentage to set.
|
||||||
power percentage.
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting the power percentage.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not 0 < percent < 100:
|
if not 0 < percent < 100:
|
||||||
return APIError(
|
raise APIError(
|
||||||
f"Power PCT % is outside of the allowed "
|
f"Power PCT % is outside of the allowed "
|
||||||
f"range. Please set a % between 0 and "
|
f"range. Please set a % between 0 and "
|
||||||
f"100"
|
f"100"
|
||||||
@@ -597,22 +686,29 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
return await self.send_command(enc_command)
|
return await self.send_command(enc_command)
|
||||||
|
|
||||||
async def pre_power_on(self, complete: bool, msg: str):
|
async def pre_power_on(self, complete: bool, msg: str) -> dict:
|
||||||
"""Configure or check status of pre power on.
|
"""Configure or check status of pre power on.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Configure or check status of pre power on, only works after
|
Configure or check status of pre power on, only works after
|
||||||
changing the password of the miner using the Whatsminer tool.
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
:param complete: check whether pre power on is complete.
|
Parameters:
|
||||||
:param msg: the message to check.
|
complete: check whether pre power on is complete.
|
||||||
"wait for adjust temp" or
|
msg: ## the message to check.
|
||||||
"adjust complete" or
|
* `wait for adjust temp`
|
||||||
"adjust continue"
|
* `adjust complete`
|
||||||
:return: A reply informing of the status of pre power on.
|
* `adjust continue`
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of pre power on.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not msg == "wait for adjust temp" or "adjust complete" or "adjust continue":
|
if not msg == "wait for adjust temp" or "adjust complete" or "adjust continue":
|
||||||
return APIError(
|
raise APIError(
|
||||||
"Message is incorrect, please choose one of "
|
"Message is incorrect, please choose one of "
|
||||||
'["wait for adjust temp", '
|
'["wait for adjust temp", '
|
||||||
'"adjust complete", '
|
'"adjust complete", '
|
||||||
@@ -629,77 +725,126 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
#### END privileged COMMANDS ####
|
#### END privileged COMMANDS ####
|
||||||
|
|
||||||
async def summary(self):
|
async def summary(self) -> dict:
|
||||||
"""Get the summary status from the miner.
|
"""Get the summary status from the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Summary status of the miner.
|
Returns:
|
||||||
|
|
||||||
|
Summary status of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("summary")
|
return await self.send_command("summary")
|
||||||
|
|
||||||
async def pools(self):
|
async def pools(self) -> dict:
|
||||||
"""Get the pool status from the miner.
|
"""Get the pool status from the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Pool status of the miner.
|
Returns:
|
||||||
|
|
||||||
|
Pool status of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pools")
|
return await self.send_command("pools")
|
||||||
|
|
||||||
async def devs(self):
|
async def devs(self) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details.
|
"""Get data on each PGA/ASC with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devs")
|
return await self.send_command("devs")
|
||||||
|
|
||||||
async def edevs(self):
|
async def edevs(self) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details, ignoring
|
"""Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
|
||||||
blacklisted and zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("edevs")
|
return await self.send_command("edevs")
|
||||||
|
|
||||||
async def devdetails(self):
|
async def devdetails(self) -> dict:
|
||||||
"""Get data on all devices with their static details.
|
"""Get data on all devices with their static details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all devices with their static details.
|
Returns:
|
||||||
|
|
||||||
|
Data on all devices with their static details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devdetails")
|
return await self.send_command("devdetails")
|
||||||
|
|
||||||
async def get_psu(self):
|
async def get_psu(self) -> dict:
|
||||||
"""Get data on the PSU and power information.
|
"""Get data on the PSU and power information.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on the PSU and power information.
|
Returns:
|
||||||
|
|
||||||
|
Data on the PSU and power information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("get_psu")
|
return await self.send_command("get_psu")
|
||||||
|
|
||||||
async def version(self):
|
async def version(self) -> dict:
|
||||||
"""Get version data for the miner.
|
"""Get version data for the miner. Wraps `self.get_version()`.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Get version data for the miner. This calls another function,
|
Get version data for the miner. This calls another function,
|
||||||
self.get_version(), but is named version to stay consistent
|
self.get_version(), but is named version to stay consistent
|
||||||
with the other miner APIs.
|
with the other miner APIs.
|
||||||
|
|
||||||
:return: Version data for the miner.
|
Returns:
|
||||||
|
|
||||||
|
Version data for the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.get_version()
|
return await self.get_version()
|
||||||
|
|
||||||
async def get_version(self):
|
async def get_version(self) -> dict:
|
||||||
"""Get version data for the miner.
|
"""Get version data for the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Version data for the miner.
|
Returns:
|
||||||
|
|
||||||
|
Version data for the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("get_version")
|
return await self.send_command("get_version")
|
||||||
|
|
||||||
async def status(self):
|
async def status(self) -> dict:
|
||||||
"""Get BTMiner status and firmware version.
|
"""Get BTMiner status and firmware version.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: BTMiner status and firmware version.
|
Returns:
|
||||||
|
|
||||||
|
BTMiner status and firmware version.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("status")
|
return await self.send_command("status")
|
||||||
|
|
||||||
async def get_miner_info(self):
|
async def get_miner_info(self) -> dict:
|
||||||
"""Get general miner info.
|
"""Get general miner info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: General miner info.
|
Returns:
|
||||||
|
|
||||||
|
General miner info.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("get_miner_info")
|
return await self.send_command("get_miner_info")
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
Each method corresponds to an API command in GGMiner.
|
Each method corresponds to an API command in GGMiner.
|
||||||
|
|
||||||
CGMiner API documentation:
|
[CGMiner API documentation](https://github.com/ckolivas/cgminer/blob/master/API-README)
|
||||||
https://github.com/ckolivas/cgminer/blob/master/API-README
|
|
||||||
|
|
||||||
This class abstracts use of the CGMiner API, as well as the
|
This class abstracts use of the CGMiner API, as well as the
|
||||||
methods for sending commands to it. The self.send_command()
|
methods for sending commands to it. The self.send_command()
|
||||||
@@ -15,111 +14,163 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
as such is the base for many of the functions in this class, which
|
as such is the base for many of the functions in this class, which
|
||||||
rely on it to send the command for them.
|
rely on it to send the command for them.
|
||||||
|
|
||||||
:param ip: The IP of the miner to reference the API on.
|
Parameters:
|
||||||
:param port: The port to reference the API on. Default is 4028.
|
ip: The IP of the miner to reference the API on.
|
||||||
|
port: The port to reference the API on. Default is 4028.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip, port=4028):
|
def __init__(self, ip: str, port: int = 4028):
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port)
|
||||||
|
|
||||||
async def version(self) -> dict:
|
async def version(self) -> dict:
|
||||||
"""Get miner version info.
|
"""Get miner version info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Miner version information.
|
Returns:
|
||||||
|
Miner version information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("version")
|
return await self.send_command("version")
|
||||||
|
|
||||||
async def config(self) -> dict:
|
async def config(self) -> dict:
|
||||||
"""Get some basic configuration info.
|
"""Get some basic configuration info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Some miner configuration information:
|
Returns:
|
||||||
ASC Count <- the number of ASCs
|
## Some miner configuration information:
|
||||||
PGA Count <- the number of PGAs
|
* ASC Count <- the number of ASCs
|
||||||
Pool Count <- the number of Pools
|
* PGA Count <- the number of PGAs
|
||||||
Strategy <- the current pool strategy
|
* Pool Count <- the number of Pools
|
||||||
Log Interval <- the interval of logging
|
* Strategy <- the current pool strategy
|
||||||
Device Code <- list of compiled device drivers
|
* Log Interval <- the interval of logging
|
||||||
OS <- the current operating system
|
* Device Code <- list of compiled device drivers
|
||||||
|
* OS <- the current operating system
|
||||||
|
* Failover-Only <- failover-only setting
|
||||||
|
* Scan Time <- scan-time setting
|
||||||
|
* Queue <- queue setting
|
||||||
|
* Expiry <- expiry setting
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("config")
|
return await self.send_command("config")
|
||||||
|
|
||||||
async def summary(self) -> dict:
|
async def summary(self) -> dict:
|
||||||
"""Get the status summary of the miner.
|
"""Get the status summary of the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The status summary of the miner.
|
Returns:
|
||||||
|
The status summary of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("summary")
|
return await self.send_command("summary")
|
||||||
|
|
||||||
async def pools(self) -> dict:
|
async def pools(self) -> dict:
|
||||||
"""Get pool information.
|
"""Get pool information.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Miner pool information.
|
Returns:
|
||||||
|
Miner pool information.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pools")
|
return await self.send_command("pools")
|
||||||
|
|
||||||
async def devs(self) -> dict:
|
async def devs(self) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details.
|
"""Get data on each PGA/ASC with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devs")
|
return await self.send_command("devs")
|
||||||
|
|
||||||
async def edevs(self, old: bool = False) -> dict:
|
async def edevs(self, old: bool = False) -> dict:
|
||||||
"""Get data on each PGA/ASC with their details, ignoring
|
"""Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
|
||||||
blacklisted and zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param old: Include zombie devices that became zombies less
|
Parameters:
|
||||||
than 'old' seconds ago
|
old: Include zombie devices that became zombies less than 'old' seconds ago
|
||||||
|
|
||||||
:return: Data on each PGA/ASC with their details.
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if old:
|
if old:
|
||||||
return await self.send_command("edevs", parameters="old")
|
return await self.send_command("edevs", parameters=old)
|
||||||
else:
|
else:
|
||||||
return await self.send_command("edevs")
|
return await self.send_command("edevs")
|
||||||
|
|
||||||
async def pga(self, n: int) -> dict:
|
async def pga(self, n: int) -> dict:
|
||||||
"""Get data from PGA n.
|
"""Get data from PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA number to get data from.
|
Parameters:
|
||||||
|
n: The PGA number to get data from.
|
||||||
|
|
||||||
:return: Data on the PGA n.
|
Returns:
|
||||||
|
Data on the PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pga", parameters=n)
|
return await self.send_command("pga", parameters=n)
|
||||||
|
|
||||||
async def pgacount(self) -> dict:
|
async def pgacount(self) -> dict:
|
||||||
"""Get data fon all PGAs.
|
"""Get data fon all PGAs.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on the PGAs connected.
|
Returns:
|
||||||
|
Data on the PGAs connected.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgacount")
|
return await self.send_command("pgacount")
|
||||||
|
|
||||||
async def switchpool(self, n: int) -> dict:
|
async def switchpool(self, n: int) -> dict:
|
||||||
"""Switch pools to pool n.
|
"""Switch pools to pool n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The pool to switch to.
|
Parameters:
|
||||||
|
n: The pool to switch to.
|
||||||
|
|
||||||
:return: A confirmation of switching to pool n.
|
Returns:
|
||||||
|
A confirmation of switching to pool n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("switchpool", parameters=n)
|
return await self.send_command("switchpool", parameters=n)
|
||||||
|
|
||||||
async def enablepool(self, n: int) -> dict:
|
async def enablepool(self, n: int) -> dict:
|
||||||
"""Enable pool n.
|
"""Enable pool n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The pool to enable.
|
Parameters:
|
||||||
|
n: The pool to enable.
|
||||||
|
|
||||||
:return: A confirmation of enabling pool n.
|
Returns:
|
||||||
|
A confirmation of enabling pool n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("enablepool", parameters=n)
|
return await self.send_command("enablepool", parameters=n)
|
||||||
|
|
||||||
async def addpool(self, url: str, username: str, password: str) -> dict:
|
async def addpool(self, url: str, username: str, password: str) -> dict:
|
||||||
"""Add a pool to the miner.
|
"""Add a pool to the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param url: The URL of the new pool to add.
|
Parameters:
|
||||||
:param username: The users username on the new pool.
|
url: The URL of the new pool to add.
|
||||||
:param password: The worker password on the new pool.
|
username: The users username on the new pool.
|
||||||
|
password: The worker password on the new pool.
|
||||||
|
|
||||||
:return: A confirmation of adding the pool.
|
Returns:
|
||||||
|
A confirmation of adding the pool.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command(
|
return await self.send_command(
|
||||||
"addpool", parameters=f"{url},{username},{password}"
|
"addpool", parameters=f"{url},{username},{password}"
|
||||||
@@ -127,48 +178,73 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def poolpriority(self, *n: int) -> dict:
|
async def poolpriority(self, *n: int) -> dict:
|
||||||
"""Set pool priority.
|
"""Set pool priority.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pools in order of priority.
|
Parameters:
|
||||||
|
*n: Pools in order of priority.
|
||||||
|
|
||||||
:return: A confirmation of setting pool priority.
|
Returns:
|
||||||
|
A confirmation of setting pool priority.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
pools = f"{','.join([str(item) for item in n])}"
|
pools = f"{','.join([str(item) for item in n])}"
|
||||||
return await self.send_command("poolpriority", parameters=pools)
|
return await self.send_command("poolpriority", parameters=pools)
|
||||||
|
|
||||||
async def poolquota(self, n: int, q: int) -> dict:
|
async def poolquota(self, n: int, q: int) -> dict:
|
||||||
"""Set pool quota.
|
"""Set pool quota.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pool number to set quota on.
|
Parameters:
|
||||||
:param q: Quota to set the pool to.
|
n: Pool number to set quota on.
|
||||||
|
q: Quota to set the pool to.
|
||||||
|
|
||||||
:return: A confirmation of setting pool quota.
|
Returns:
|
||||||
|
A confirmation of setting pool quota.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("poolquota", parameters=f"{n},{q}")
|
return await self.send_command("poolquota", parameters=f"{n},{q}")
|
||||||
|
|
||||||
async def disablepool(self, n: int) -> dict:
|
async def disablepool(self, n: int) -> dict:
|
||||||
"""Disable a pool.
|
"""Disable a pool.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pool to disable.
|
Parameters:
|
||||||
|
n: Pool to disable.
|
||||||
|
|
||||||
:return: A confirmation of diabling the pool.
|
Returns:
|
||||||
|
A confirmation of diabling the pool.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("disablepool", parameters=n)
|
return await self.send_command("disablepool", parameters=n)
|
||||||
|
|
||||||
async def removepool(self, n: int) -> dict:
|
async def removepool(self, n: int) -> dict:
|
||||||
"""Remove a pool.
|
"""Remove a pool.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: Pool to remove.
|
Parameters:
|
||||||
|
n: Pool to remove.
|
||||||
|
|
||||||
:return: A confirmation of removing the pool.
|
Returns:
|
||||||
|
A confirmation of removing the pool.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("removepool", parameters=n)
|
return await self.send_command("removepool", parameters=n)
|
||||||
|
|
||||||
async def save(self, filename: str = None) -> dict:
|
async def save(self, filename: str = None) -> dict:
|
||||||
"""Save the config.
|
"""Save the config.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param filename: Filename to save the config as.
|
Parameters:
|
||||||
|
filename: Filename to save the config as.
|
||||||
|
|
||||||
:return: A confirmation of saving the config.
|
Returns:
|
||||||
|
A confirmation of saving the config.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if filename:
|
if filename:
|
||||||
return await self.send_command("save", parameters=filename)
|
return await self.send_command("save", parameters=filename)
|
||||||
@@ -177,83 +253,123 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def quit(self) -> dict:
|
async def quit(self) -> dict:
|
||||||
"""Quit CGMiner.
|
"""Quit CGMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: A single "BYE" before CGMiner quits.
|
Returns:
|
||||||
|
A single "BYE" before CGMiner quits.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("quit")
|
return await self.send_command("quit")
|
||||||
|
|
||||||
async def notify(self) -> dict:
|
async def notify(self) -> dict:
|
||||||
"""Notify the user of past errors.
|
"""Notify the user of past errors.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The last status and count of each devices problem(s).
|
Returns:
|
||||||
|
The last status and count of each devices problem(s).
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("notify")
|
return await self.send_command("notify")
|
||||||
|
|
||||||
async def privileged(self) -> dict:
|
async def privileged(self) -> dict:
|
||||||
"""Check if you have privileged access.
|
"""Check if you have privileged access.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The STATUS section with an error if you have no
|
Returns:
|
||||||
privileged access, or success if you have privileged access.
|
The STATUS section with an error if you have no privileged access, or success if you have privileged access.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("privileged")
|
return await self.send_command("privileged")
|
||||||
|
|
||||||
async def pgaenable(self, n: int) -> dict:
|
async def pgaenable(self, n: int) -> dict:
|
||||||
"""Enable PGA n.
|
"""Enable PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA to enable.
|
Parameters:
|
||||||
|
n: The PGA to enable.
|
||||||
|
|
||||||
:return: A confirmation of enabling PGA n.
|
Returns:
|
||||||
|
A confirmation of enabling PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgaenable", parameters=n)
|
return await self.send_command("pgaenable", parameters=n)
|
||||||
|
|
||||||
async def pgadisable(self, n: int) -> dict:
|
async def pgadisable(self, n: int) -> dict:
|
||||||
"""Disable PGA n.
|
"""Disable PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA to disable.
|
Parameters:
|
||||||
|
n: The PGA to disable.
|
||||||
|
|
||||||
:return: A confirmation of disabling PGA n.
|
Returns:
|
||||||
|
A confirmation of disabling PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgadisable", parameters=n)
|
return await self.send_command("pgadisable", parameters=n)
|
||||||
|
|
||||||
async def pgaidentify(self, n: int) -> dict:
|
async def pgaidentify(self, n: int) -> dict:
|
||||||
"""Identify PGA n.
|
"""Identify PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The PGA to identify.
|
Parameters:
|
||||||
|
n: The PGA to identify.
|
||||||
|
|
||||||
:return: A confirmation of identifying PGA n.
|
Returns:
|
||||||
|
A confirmation of identifying PGA n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("pgaidentify", parameters=n)
|
return await self.send_command("pgaidentify", parameters=n)
|
||||||
|
|
||||||
async def devdetails(self) -> dict:
|
async def devdetails(self) -> dict:
|
||||||
"""Get data on all devices with their static details.
|
"""Get data on all devices with their static details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all devices with their static details.
|
Returns:
|
||||||
|
Data on all devices with their static details.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("devdetails")
|
return await self.send_command("devdetails")
|
||||||
|
|
||||||
async def restart(self) -> dict:
|
async def restart(self) -> dict:
|
||||||
"""Restart CGMiner using the API.
|
"""Restart CGMiner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: A reply informing of the restart.
|
Returns:
|
||||||
|
A reply informing of the restart.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("restart")
|
return await self.send_command("restart")
|
||||||
|
|
||||||
async def stats(self) -> dict:
|
async def stats(self) -> dict:
|
||||||
"""Get stats of each device/pool with more than 1 getwork.
|
"""Get stats of each device/pool with more than 1 getwork.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Stats of each device/pool with more than 1 getwork.
|
Returns:
|
||||||
|
Stats of each device/pool with more than 1 getwork.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("stats")
|
return await self.send_command("stats")
|
||||||
|
|
||||||
async def estats(self, old: bool = False) -> dict:
|
async def estats(self, old: bool = False) -> dict:
|
||||||
"""Get stats of each device/pool with more than 1 getwork,
|
"""Get stats of each device/pool with more than 1 getwork, ignoring zombie devices.
|
||||||
ignoring zombie devices.
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param old: Include zombie devices that became zombies less
|
Parameters:
|
||||||
than 'old' seconds ago.
|
old: Include zombie devices that became zombies less than 'old' seconds ago.
|
||||||
|
|
||||||
:return: Stats of each device/pool with more than 1 getwork,
|
Returns:
|
||||||
ignoring zombie devices.
|
Stats of each device/pool with more than 1 getwork, ignoring zombie devices.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if old:
|
if old:
|
||||||
return await self.send_command("estats", parameters=old)
|
return await self.send_command("estats", parameters=old)
|
||||||
@@ -262,92 +378,126 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def check(self, command: str) -> dict:
|
async def check(self, command: str) -> dict:
|
||||||
"""Check if the command command exists in CGMiner.
|
"""Check if the command command exists in CGMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param command: The command to check.
|
Parameters:
|
||||||
|
command: The command to check.
|
||||||
|
|
||||||
:return: Information about a command:
|
Returns:
|
||||||
Exists (Y/N) <- the command exists in this version
|
## Information about a command:
|
||||||
Access (Y/N) <- you have access to use the command
|
* Exists (Y/N) <- the command exists in this version
|
||||||
|
* Access (Y/N) <- you have access to use the command
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("check", parameters=command)
|
return await self.send_command("check", parameters=command)
|
||||||
|
|
||||||
async def failover_only(self, failover: bool) -> dict:
|
async def failover_only(self, failover: bool) -> dict:
|
||||||
"""Set failover-only.
|
"""Set failover-only.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
failover: What to set failover-only to.
|
||||||
|
|
||||||
:param failover: What to set failover-only to.
|
Returns:
|
||||||
|
Confirmation of setting failover-only.
|
||||||
:return: Confirmation of setting failover-only.
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("failover-only", parameters=failover)
|
return await self.send_command("failover-only", parameters=failover)
|
||||||
|
|
||||||
async def coin(self) -> dict:
|
async def coin(self) -> dict:
|
||||||
"""Get information on the current coin.
|
"""Get information on the current coin.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Information about the current coin being mined:
|
Returns:
|
||||||
Hash Method <- the hashing algorithm
|
## Information about the current coin being mined:
|
||||||
Current Block Time <- blocktime as a float, 0 means none
|
* Hash Method <- the hashing algorithm
|
||||||
Current Block Hash <- the hash of the current block, blank
|
* Current Block Time <- blocktime as a float, 0 means none
|
||||||
means none
|
* Current Block Hash <- the hash of the current block, blank means none
|
||||||
LP <- whether LP is in use on at least 1 pool
|
* LP <- whether LP is in use on at least 1 pool
|
||||||
Network Difficulty: the current network difficulty
|
* Network Difficulty: the current network difficulty
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("coin")
|
return await self.send_command("coin")
|
||||||
|
|
||||||
async def debug(self, setting: str) -> dict:
|
async def debug(self, setting: str) -> dict:
|
||||||
"""Set a debug setting.
|
"""Set a debug setting.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param setting: Which setting to switch to. Options are:
|
Parameters:
|
||||||
Silent,
|
setting: Which setting to switch to.
|
||||||
Quiet,
|
## Options are:
|
||||||
Verbose,
|
* Silent
|
||||||
Debug,
|
* Quiet
|
||||||
RPCProto,
|
* Verbose
|
||||||
PerDevice,
|
* Debug
|
||||||
WorkTime,
|
* RPCProto
|
||||||
Normal.
|
* PerDevice
|
||||||
|
* WorkTime
|
||||||
|
* Normal
|
||||||
|
|
||||||
:return: Data on which debug setting was enabled or disabled.
|
Returns:
|
||||||
|
Data on which debug setting was enabled or disabled.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("debug", parameters=setting)
|
return await self.send_command("debug", parameters=setting)
|
||||||
|
|
||||||
async def setconfig(self, name: str, n: int) -> dict:
|
async def setconfig(self, name: str, n: int) -> dict:
|
||||||
"""Set config of name to value n.
|
"""Set config of name to value n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param name: The name of the config setting to set. Options are:
|
Parameters:
|
||||||
queue,
|
name: The name of the config setting to set.
|
||||||
scantime,
|
## Options are:
|
||||||
expiry.
|
* queue
|
||||||
:param n: The value to set the 'name' setting to.
|
* scantime
|
||||||
|
* expiry
|
||||||
|
n: The value to set the 'name' setting to.
|
||||||
|
|
||||||
:return: The results of setting config of name to n.
|
Returns:
|
||||||
|
The results of setting config of name to n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("setconfig", parameters=f"{name},{n}")
|
return await self.send_command("setconfig", parameters=f"{name},{n}")
|
||||||
|
|
||||||
async def usbstats(self) -> dict:
|
async def usbstats(self) -> dict:
|
||||||
"""Get stats of all USB devices except ztex.
|
"""Get stats of all USB devices except ztex.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The stats of all USB devices except ztex.
|
Returns:
|
||||||
|
The stats of all USB devices except ztex.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("usbstats")
|
return await self.send_command("usbstats")
|
||||||
|
|
||||||
async def pgaset(self, n: int, opt: str, val: int = None) -> dict:
|
async def pgaset(self, n: int, opt: str, val: int = None) -> dict:
|
||||||
"""Set PGA option opt to val on PGA n.
|
"""Set PGA option opt to val on PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
```
|
||||||
MMQ -
|
MMQ -
|
||||||
opt: clock
|
opt: clock
|
||||||
val: 160 - 230 (multiple of 2)
|
val: 160 - 230 (multiple of 2)
|
||||||
CMR -
|
CMR -
|
||||||
opt: clock
|
opt: clock
|
||||||
val: 100 - 220
|
val: 100 - 220
|
||||||
|
```
|
||||||
|
|
||||||
:param n: The PGA to set the options on.
|
Parameters:
|
||||||
:param opt: The option to set. Setting this to 'help'
|
n: The PGA to set the options on.
|
||||||
returns a help message.
|
opt: The option to set. Setting this to 'help' returns a help message.
|
||||||
:param val: The value to set the option to.
|
val: The value to set the option to.
|
||||||
|
|
||||||
:return: Confirmation of setting PGA n with opt[,val].
|
Returns:
|
||||||
|
Confirmation of setting PGA n with opt[,val].
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
if val:
|
if val:
|
||||||
return await self.send_command("pgaset", parameters=f"{n},{opt},{val}")
|
return await self.send_command("pgaset", parameters=f"{n},{opt},{val}")
|
||||||
@@ -356,75 +506,108 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def zero(self, which: str, summary: bool) -> dict:
|
async def zero(self, which: str, summary: bool) -> dict:
|
||||||
"""Zero a device.
|
"""Zero a device.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param which: Which device to zero.
|
Parameters:
|
||||||
Setting this to 'all' zeros all devices.
|
which: Which device to zero. Setting this to 'all' zeros all devices. Setting this to 'bestshare' zeros only the bestshare values for each pool and global.
|
||||||
Setting this to 'bestshare' zeros only the bestshare values
|
summary: Whether or not to show a full summary.
|
||||||
for each pool and global.
|
|
||||||
:param summary: Whether or not to show a full summary.
|
|
||||||
|
|
||||||
|
|
||||||
:return: the STATUS section with info on the zero and optional
|
Returns:
|
||||||
summary.
|
the STATUS section with info on the zero and optional summary.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("zero", parameters=f"{which},{summary}")
|
return await self.send_command("zero", parameters=f"{which},{summary}")
|
||||||
|
|
||||||
async def hotplug(self, n: int) -> dict:
|
async def hotplug(self, n: int) -> dict:
|
||||||
"""Enable hotplug.
|
"""Enable hotplug.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device number to set hotplug on.
|
Parameters:
|
||||||
|
n: The device number to set hotplug on.
|
||||||
|
|
||||||
:return: Information on hotplug status.
|
Returns:
|
||||||
|
Information on hotplug status.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("hotplug", parameters=n)
|
return await self.send_command("hotplug", parameters=n)
|
||||||
|
|
||||||
async def asc(self, n: int) -> dict:
|
async def asc(self, n: int) -> dict:
|
||||||
"""Get data for ASC device n.
|
"""Get data for ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to get data for.
|
Parameters:
|
||||||
|
n: The device to get data for.
|
||||||
|
|
||||||
:return: The data for ASC device n.
|
Returns:
|
||||||
|
The data for ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("asc", parameters=n)
|
return await self.send_command("asc", parameters=n)
|
||||||
|
|
||||||
async def ascenable(self, n: int) -> dict:
|
async def ascenable(self, n: int) -> dict:
|
||||||
"""Enable ASC device n.
|
"""Enable ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to enable.
|
Parameters:
|
||||||
|
n: The device to enable.
|
||||||
|
|
||||||
:return: Confirmation of enabling ASC device n.
|
Returns:
|
||||||
|
Confirmation of enabling ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("ascenable", parameters=n)
|
return await self.send_command("ascenable", parameters=n)
|
||||||
|
|
||||||
async def ascdisable(self, n: int) -> dict:
|
async def ascdisable(self, n: int) -> dict:
|
||||||
"""Disable ASC device n.
|
"""Disable ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to disable.
|
Parameters:
|
||||||
|
n: The device to disable.
|
||||||
|
|
||||||
:return: Confirmation of disabling ASC device n.
|
Returns:
|
||||||
|
Confirmation of disabling ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("ascdisable", parameters=n)
|
return await self.send_command("ascdisable", parameters=n)
|
||||||
|
|
||||||
async def ascidentify(self, n: int) -> dict:
|
async def ascidentify(self, n: int) -> dict:
|
||||||
"""Identify ASC device n.
|
"""Identify ASC device n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:param n: The device to identify.
|
Parameters:
|
||||||
|
n: The device to identify.
|
||||||
|
|
||||||
:return: Confirmation of identifying ASC device n.
|
Returns:
|
||||||
|
Confirmation of identifying ASC device n.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("ascidentify", parameters=n)
|
return await self.send_command("ascidentify", parameters=n)
|
||||||
|
|
||||||
async def asccount(self) -> dict:
|
async def asccount(self) -> dict:
|
||||||
"""Get data on the number of ASC devices and their info.
|
"""Get data on the number of ASC devices and their info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: Data on all ASC devices.
|
Returns:
|
||||||
|
Data on all ASC devices.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("asccount")
|
return await self.send_command("asccount")
|
||||||
|
|
||||||
async def ascset(self, n: int, opt: str, val: int = None) -> dict:
|
async def ascset(self, n: int, opt: str, val: int = None) -> dict:
|
||||||
"""Set ASC n option opt to value val.
|
"""Set ASC n option opt to value val.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
Sets an option on the ASC n to a value. Allowed options are:
|
Sets an option on the ASC n to a value. Allowed options are:
|
||||||
|
```
|
||||||
AVA+BTB -
|
AVA+BTB -
|
||||||
opt: freq
|
opt: freq
|
||||||
val: 256 - 1024 (chip frequency)
|
val: 256 - 1024 (chip frequency)
|
||||||
@@ -458,14 +641,16 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
opt: clock
|
opt: clock
|
||||||
val: 0 - 15
|
val: 0 - 15
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The ASC to set the options on.
|
||||||
|
opt: The option to set. Setting this to 'help' returns a help message.
|
||||||
|
val: The value to set the option to.
|
||||||
|
|
||||||
:param n: The ASC to set the options on.
|
Returns:
|
||||||
:param opt: The option to set. Setting this to 'help' returns a
|
Confirmation of setting option opt to value val.
|
||||||
help message.
|
</details>
|
||||||
:param val: The value to set the option to.
|
|
||||||
|
|
||||||
:return: Confirmation of setting option opt to value val.
|
|
||||||
"""
|
"""
|
||||||
if val:
|
if val:
|
||||||
return await self.send_command("ascset", parameters=f"{n},{opt},{val}")
|
return await self.send_command("ascset", parameters=f"{n},{opt},{val}")
|
||||||
@@ -474,14 +659,22 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def lcd(self) -> dict:
|
async def lcd(self) -> dict:
|
||||||
"""Get a general all-in-one status summary of the miner.
|
"""Get a general all-in-one status summary of the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: An all-in-one status summary of the miner.
|
Returns:
|
||||||
|
An all-in-one status summary of the miner.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("lcd")
|
return await self.send_command("lcd")
|
||||||
|
|
||||||
async def lockstats(self) -> dict:
|
async def lockstats(self) -> dict:
|
||||||
"""Write lockstats to STDERR.
|
"""Write lockstats to STDERR.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
:return: The result of writing the lock stats to STDERR.
|
Returns:
|
||||||
|
The result of writing the lock stats to STDERR.
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_command("lockstats")
|
return await self.send_command("lockstats")
|
||||||
|
|||||||
@@ -4,34 +4,44 @@ from datetime import datetime
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MinerData:
|
class MinerData:
|
||||||
"""A Dataclass to standardize data returned from miners (specifically AnyMiner().get_data())
|
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
|
||||||
|
|
||||||
:param ip: The IP of the miner as a str.
|
Attributes:
|
||||||
:param datetime: The time and date this data was generated.
|
ip: The IP of the miner as a str.
|
||||||
:param model: The model of the miner as a str.
|
datetime: The time and date this data was generated.
|
||||||
:param hostname: The network hostname of the miner as a str.
|
model: The model of the miner as a str.
|
||||||
:param hashrate: The hashrate of the miner in TH/s as a int.
|
hostname: The network hostname of the miner as a str.
|
||||||
:param left_board_temp: The temp of the left PCB as an int.
|
hashrate: The hashrate of the miner in TH/s as a float.
|
||||||
:param left_board_chip_temp: The temp of the left board chips as an int.
|
left_board_hashrate: The hashrate of the left board of the miner in TH/s as a float.
|
||||||
:param center_board_temp: The temp of the center PCB as an int.
|
center_board_hashrate: The hashrate of the center board of the miner in TH/s as a float.
|
||||||
:param center_board_chip_temp: The temp of the center board chips as an int.
|
right_board_hashrate: The hashrate of the right board of the miner in TH/s as a float.
|
||||||
:param right_board_temp: The temp of the right PCB as an int.
|
temperature_avg: The average temperature across the boards. Calculated automatically.
|
||||||
:param right_board_chip_temp: The temp of the right board chips as an int.
|
env_temp: The environment temps as a float.
|
||||||
:param wattage: Current power draw of the miner as an int.
|
left_board_temp: The temp of the left PCB as an int.
|
||||||
:param wattage_limit: Power limit of the miner as an int.
|
left_board_chip_temp: The temp of the left board chips as an int.
|
||||||
:param fan_1: The speed of the first fan as an int.
|
center_board_temp: The temp of the center PCB as an int.
|
||||||
:param fan_2: The speed of the second fan as an int.
|
center_board_chip_temp: The temp of the center board chips as an int.
|
||||||
:param fan_3: The speed of the third fan as an int.
|
right_board_temp: The temp of the right PCB as an int.
|
||||||
:param fan_4: The speed of the fourth fan as an int.
|
right_board_chip_temp: The temp of the right board chips as an int.
|
||||||
:param left_chips: The number of chips online in the left board as an int.
|
wattage: Current power draw of the miner as an int.
|
||||||
:param center_chips: The number of chips online in the left board as an int.
|
wattage_limit: Power limit of the miner as an int.
|
||||||
:param right_chips: The number of chips online in the left board as an int.
|
fan_1: The speed of the first fan as an int.
|
||||||
:param ideal_chips: The ideal number of chips in the miner as an int.
|
fan_2: The speed of the second fan as an int.
|
||||||
:param pool_split: The pool split as a str.
|
fan_3: The speed of the third fan as an int.
|
||||||
:param pool_1_url: The first pool url on the miner as a str.
|
fan_4: The speed of the fourth fan as an int.
|
||||||
:param pool_1_user: The first pool user on the miner as a str.
|
left_chips: The number of chips online in the left board as an int.
|
||||||
:param pool_2_url: The second pool url on the miner as a str.
|
center_chips: The number of chips online in the left board as an int.
|
||||||
:param pool_2_user: The second pool user on the miner as a str.
|
right_chips: The number of chips online in the left board as an int.
|
||||||
|
total_chips: The total number of chips on all boards. Calculated automatically.
|
||||||
|
ideal_chips: The ideal number of chips in the miner as an int.
|
||||||
|
perecent_ideal: The percent of total chips out of the ideal count. Calculated automatically.
|
||||||
|
nominal: The nominal amount of chips in the miner. Calculated automatically.
|
||||||
|
pool_split: The pool split as a str.
|
||||||
|
pool_1_url: The first pool url on the miner as a str.
|
||||||
|
pool_1_user: The first pool user on the miner as a str.
|
||||||
|
pool_2_url: The second pool url on the miner as a str.
|
||||||
|
pool_2_user: The second pool user on the miner as a str.
|
||||||
|
errors: A list of errors on the miner.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ip: str
|
ip: str
|
||||||
@@ -40,6 +50,9 @@ class MinerData:
|
|||||||
model: str = "Unknown"
|
model: str = "Unknown"
|
||||||
hostname: str = "Unknown"
|
hostname: str = "Unknown"
|
||||||
hashrate: float = 0
|
hashrate: float = 0
|
||||||
|
left_board_hashrate: float = 0
|
||||||
|
center_board_hashrate: float = 0
|
||||||
|
right_board_hashrate: float = 0
|
||||||
temperature_avg: int = field(init=False)
|
temperature_avg: int = field(init=False)
|
||||||
env_temp: float = 0
|
env_temp: float = 0
|
||||||
left_board_temp: int = 0
|
left_board_temp: int = 0
|
||||||
@@ -66,6 +79,7 @@ class MinerData:
|
|||||||
pool_1_user: str = "Unknown"
|
pool_1_user: str = "Unknown"
|
||||||
pool_2_url: str = ""
|
pool_2_url: str = ""
|
||||||
pool_2_user: str = ""
|
pool_2_user: str = ""
|
||||||
|
errors: list = field(default_factory=list)
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
self.datetime = datetime.now()
|
self.datetime = datetime.now()
|
||||||
|
|||||||
2
pyasic/data/error_codes/__init__.py
Normal file
2
pyasic/data/error_codes/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .whatsminer import WhatsminerError
|
||||||
|
from .bos import BraiinsOSError
|
||||||
11
pyasic/data/error_codes/bos.py
Normal file
11
pyasic/data/error_codes/bos.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BraiinsOSError:
|
||||||
|
"""A Dataclass to handle error codes of BraiinsOS+ miners."""
|
||||||
|
|
||||||
|
error_message: str
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return asdict(self)
|
||||||
152
pyasic/data/error_codes/whatsminer.py
Normal file
152
pyasic/data/error_codes/whatsminer.py
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
from dataclasses import dataclass, field, asdict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WhatsminerError:
|
||||||
|
"""A Dataclass to handle error codes of Whatsminers."""
|
||||||
|
|
||||||
|
error_code: int
|
||||||
|
error_message: str = field(init=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def error_message(self): # noqa - Skip PyCharm inspection
|
||||||
|
if self.error_code in ERROR_CODES:
|
||||||
|
return ERROR_CODES[self.error_code]
|
||||||
|
return "Unknown error type."
|
||||||
|
|
||||||
|
@error_message.setter
|
||||||
|
def error_message(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
|
ERROR_CODES = {
|
||||||
|
110: "Intake fan speed error.",
|
||||||
|
111: "Exhaust fan speed error.",
|
||||||
|
120: "Intake fan speed error. Fan speed deviates by more than 2000.",
|
||||||
|
121: "Exhaust fan speed error. Fan speed deviates by more than 2000.",
|
||||||
|
130: "Intake fan speed error. Fan speed deviates by more than 3000.",
|
||||||
|
131: "Exhaust fan speed error. Fan speed deviates by more than 3000.",
|
||||||
|
140: "Fan speed too high.",
|
||||||
|
200: "Power probing error. No power found.",
|
||||||
|
201: "Power supply and configuration file don't match.",
|
||||||
|
202: "Power output voltage error.",
|
||||||
|
203: "Power protecting due to high environment temperature.",
|
||||||
|
204: "Power current protecting due to high environment temperature.",
|
||||||
|
205: "Power current error.",
|
||||||
|
206: "Power input low voltage error.",
|
||||||
|
207: "Power input current protecting due to bad power input.",
|
||||||
|
210: "Power error.",
|
||||||
|
213: "Power input voltage and current do not match power output.",
|
||||||
|
216: "Power remained unchanged for a long time.",
|
||||||
|
217: "Power set enable error.",
|
||||||
|
218: "Power input voltage is lower than 230V for high power mode.",
|
||||||
|
233: "Power output high temperature protection error.",
|
||||||
|
234: "Power output high temperature protection error.",
|
||||||
|
235: "Power output high temperature protection error.",
|
||||||
|
236: "Power output high current protection error.",
|
||||||
|
237: "Power output high current protection error.",
|
||||||
|
238: "Power output high current protection error.",
|
||||||
|
239: "Power output high voltage protection error.",
|
||||||
|
240: "Power output low voltage protection error.",
|
||||||
|
241: "Power output current imbalance error.",
|
||||||
|
243: "Power input high temperature protection error.",
|
||||||
|
244: "Power input high temperature protection error.",
|
||||||
|
245: "Power input high temperature protection error.",
|
||||||
|
246: "Power input high voltage protection error.",
|
||||||
|
247: "Power input high voltage protection error.",
|
||||||
|
248: "Power input high current protection error.",
|
||||||
|
249: "Power input high current protection error.",
|
||||||
|
250: "Power input low voltage protection error.",
|
||||||
|
251: "Power input low voltage protection error.",
|
||||||
|
253: "Power supply fan error.",
|
||||||
|
254: "Power supply fan error.",
|
||||||
|
255: "Power output high power protection error.",
|
||||||
|
256: "Power output high power protection error.",
|
||||||
|
257: "Input over current protection of power supply on primary side.",
|
||||||
|
263: "Power communication warning.",
|
||||||
|
264: "Power communication error.",
|
||||||
|
267: "Power watchdog protection.",
|
||||||
|
268: "Power output high current protection.",
|
||||||
|
269: "Power input high current protection.",
|
||||||
|
270: "Power input high voltage protection.",
|
||||||
|
271: "Power input low voltage protection.",
|
||||||
|
272: "Excessive power supply output warning.",
|
||||||
|
273: "Power input too high warning.",
|
||||||
|
274: "Power fan warning.",
|
||||||
|
275: "Power high temperature warning.",
|
||||||
|
300: "Right board temperature sensor detection error.",
|
||||||
|
301: "Center board temperature sensor detection error.",
|
||||||
|
302: "Left board temperature sensor detection error.",
|
||||||
|
320: "Right board temperature reading error.",
|
||||||
|
321: "Center board temperature reading error.",
|
||||||
|
322: "Left board temperature reading error.",
|
||||||
|
329: "Control board temperature sensor communication error.",
|
||||||
|
350: "Right board temperature protecting.",
|
||||||
|
351: "Center board temperature protecting.",
|
||||||
|
352: "Left board temperature protecting.",
|
||||||
|
360: "Hashboard high temperature error.",
|
||||||
|
410: "Right board eeprom detection error.",
|
||||||
|
411: "Center board eeprom detection error.",
|
||||||
|
412: "Left board eeprom detection error.",
|
||||||
|
420: "Right board eeprom parsing error.",
|
||||||
|
421: "Center board eeprom parsing error.",
|
||||||
|
422: "Left board eeprom parsing error.",
|
||||||
|
430: "Right board chip bin type error.",
|
||||||
|
431: "Center board chip bin type error.",
|
||||||
|
432: "Left board chip bin type error.",
|
||||||
|
440: "Right board eeprom chip number X error.",
|
||||||
|
441: "Center board eeprom chip number X error.",
|
||||||
|
442: "Left board eeprom chip number X error.",
|
||||||
|
450: "Right board eeprom xfer error.",
|
||||||
|
451: "Center board eeprom xfer error.",
|
||||||
|
452: "Left board eeprom xfer error.",
|
||||||
|
510: "Right board miner type error.",
|
||||||
|
511: "Center board miner type error.",
|
||||||
|
512: "Left board miner type error.",
|
||||||
|
520: "Right board bin type error.",
|
||||||
|
521: "Center board bin type error.",
|
||||||
|
522: "Left board bin type error.",
|
||||||
|
530: "Right board not found.",
|
||||||
|
531: "Center board not found.",
|
||||||
|
532: "Left board not found.",
|
||||||
|
540: "Right board error reading chip id.",
|
||||||
|
541: "Center board error reading chip id.",
|
||||||
|
542: "Left board error reading chip id.",
|
||||||
|
550: "Right board has bad chips.",
|
||||||
|
551: "Center board has bad chips.",
|
||||||
|
552: "Left board has bad chips.",
|
||||||
|
560: "Right board loss of balance error.",
|
||||||
|
561: "Center board loss of balance error.",
|
||||||
|
562: "Left board loss of balance error.",
|
||||||
|
600: "Environment temperature is too high.",
|
||||||
|
610: "Environment temperature is too high for high performance mode.",
|
||||||
|
701: "Control board no support chip.",
|
||||||
|
710: "Control board rebooted as an exception.",
|
||||||
|
712: "Control board rebooted as an exception.",
|
||||||
|
800: "CGMiner checksum error.",
|
||||||
|
801: "System monitor checksum error.",
|
||||||
|
802: "Remote daemon checksum error.",
|
||||||
|
2010: "All pools are disabled.",
|
||||||
|
2020: "Pool 0 connection failed.",
|
||||||
|
2021: "Pool 1 connection failed.",
|
||||||
|
2022: "Pool 2 connection failed.",
|
||||||
|
2030: "High rejection rate on pool.",
|
||||||
|
2040: "The pool does not support asicboost mode.",
|
||||||
|
2310: "Hashrate is too low.",
|
||||||
|
2320: "Hashrate is too low.",
|
||||||
|
2340: "Hashrate loss is too high.",
|
||||||
|
2350: "Hashrate loss is too high.",
|
||||||
|
5070: "Right hashboard water velocity is abnormal.",
|
||||||
|
5071: "Center hashboard water velocity is abnormal.",
|
||||||
|
5072: "Left hashboard water velocity is abnormal.",
|
||||||
|
5110: "Right hashboard frequency up timeout.",
|
||||||
|
5111: "Center hashboard frequency up timeout.",
|
||||||
|
5112: "Left hashboard frequency up timeout.",
|
||||||
|
8410: "Software version error.",
|
||||||
|
100001: "/antiv/signature illegal.",
|
||||||
|
100002: "/antiv/dig/init.d illegal.",
|
||||||
|
100003: "/antiv/dig/pf_partial.dig illegal.",
|
||||||
|
}
|
||||||
@@ -186,6 +186,16 @@ class BMMiner(BaseMiner):
|
|||||||
data.center_chips = boards[1].get(f"chain_acn{board_offset+1}")
|
data.center_chips = boards[1].get(f"chain_acn{board_offset+1}")
|
||||||
data.right_chips = boards[1].get(f"chain_acn{board_offset+2}")
|
data.right_chips = boards[1].get(f"chain_acn{board_offset+2}")
|
||||||
|
|
||||||
|
data.left_board_hashrate = round(
|
||||||
|
float(boards[1].get(f"chain_rate{board_offset}")) / 1000, 2
|
||||||
|
)
|
||||||
|
data.center_board_hashrate = round(
|
||||||
|
float(boards[1].get(f"chain_rate{board_offset+1}")) / 1000, 2
|
||||||
|
)
|
||||||
|
data.right_board_hashrate = round(
|
||||||
|
float(boards[1].get(f"chain_rate{board_offset+2}")) / 1000, 2
|
||||||
|
)
|
||||||
|
|
||||||
if stats:
|
if stats:
|
||||||
temp = stats.get("STATS")
|
temp = stats.get("STATS")
|
||||||
if temp:
|
if temp:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from pyasic.miners import BaseMiner
|
|||||||
from pyasic.API.bosminer import BOSMinerAPI
|
from pyasic.API.bosminer import BOSMinerAPI
|
||||||
from pyasic.API import APIError
|
from pyasic.API import APIError
|
||||||
|
|
||||||
|
from pyasic.data.error_codes import BraiinsOSError
|
||||||
from pyasic.data import MinerData
|
from pyasic.data import MinerData
|
||||||
|
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
@@ -286,12 +287,18 @@ class BOSMiner(BaseMiner):
|
|||||||
for i in range(DATA_RETRIES):
|
for i in range(DATA_RETRIES):
|
||||||
try:
|
try:
|
||||||
miner_data = await self.api.multicommand(
|
miner_data = await self.api.multicommand(
|
||||||
"summary", "temps", "tunerstatus", "pools", "devdetails", "fans"
|
"summary",
|
||||||
|
"temps",
|
||||||
|
"tunerstatus",
|
||||||
|
"pools",
|
||||||
|
"devdetails",
|
||||||
|
"fans",
|
||||||
|
"devs",
|
||||||
)
|
)
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
if str(e.message) == "Not ready":
|
if str(e.message) == "Not ready":
|
||||||
miner_data = await self.api.multicommand(
|
miner_data = await self.api.multicommand(
|
||||||
"summary", "tunerstatus", "pools", "fans"
|
"summary", "tunerstatus", "pools", "devs"
|
||||||
)
|
)
|
||||||
if miner_data:
|
if miner_data:
|
||||||
break
|
break
|
||||||
@@ -302,6 +309,7 @@ class BOSMiner(BaseMiner):
|
|||||||
tunerstatus = miner_data.get("tunerstatus")
|
tunerstatus = miner_data.get("tunerstatus")
|
||||||
pools = miner_data.get("pools")
|
pools = miner_data.get("pools")
|
||||||
devdetails = miner_data.get("devdetails")
|
devdetails = miner_data.get("devdetails")
|
||||||
|
devs = miner_data.get("devs")
|
||||||
fans = miner_data.get("fans")
|
fans = miner_data.get("fans")
|
||||||
|
|
||||||
if summary:
|
if summary:
|
||||||
@@ -388,6 +396,30 @@ class BOSMiner(BaseMiner):
|
|||||||
if wattage:
|
if wattage:
|
||||||
data.wattage = wattage
|
data.wattage = wattage
|
||||||
|
|
||||||
|
chain_status = tuner[0].get("TunerChainStatus")
|
||||||
|
if chain_status and len(chain_status) > 0:
|
||||||
|
board_map = {
|
||||||
|
0: "Left board",
|
||||||
|
1: "Center board",
|
||||||
|
2: "Right board",
|
||||||
|
}
|
||||||
|
offset = (
|
||||||
|
6
|
||||||
|
if chain_status[0]["HashchainIndex"] in [6, 7, 8]
|
||||||
|
else chain_status[0]["HashchainIndex"]
|
||||||
|
)
|
||||||
|
for board in chain_status:
|
||||||
|
_id = board["HashchainIndex"] - offset
|
||||||
|
if board["Status"] not in [
|
||||||
|
"Stable",
|
||||||
|
"Testing performance profile",
|
||||||
|
]:
|
||||||
|
_error = board["Status"]
|
||||||
|
_error = _error[0].lower() + _error[1:]
|
||||||
|
data.errors.append(
|
||||||
|
BraiinsOSError(f"{board_map[_id]} {_error}")
|
||||||
|
)
|
||||||
|
|
||||||
if devdetails:
|
if devdetails:
|
||||||
boards = devdetails[0].get("DEVDETAILS")
|
boards = devdetails[0].get("DEVDETAILS")
|
||||||
if boards:
|
if boards:
|
||||||
@@ -399,6 +431,20 @@ class BOSMiner(BaseMiner):
|
|||||||
chips = board["Chips"]
|
chips = board["Chips"]
|
||||||
setattr(data, board_map[_id], chips)
|
setattr(data, board_map[_id], chips)
|
||||||
|
|
||||||
|
if devs:
|
||||||
|
boards = devs[0].get("DEVS")
|
||||||
|
if boards:
|
||||||
|
if len(boards) > 0:
|
||||||
|
board_map = {
|
||||||
|
0: "left_board_hashrate",
|
||||||
|
1: "center_board_hashrate",
|
||||||
|
2: "right_board_hashrate",
|
||||||
|
}
|
||||||
|
offset = 6 if boards[0]["ID"] in [6, 7, 8] else boards[0]["ID"]
|
||||||
|
for board in boards:
|
||||||
|
_id = board["ID"] - offset
|
||||||
|
hashrate = round(board["MHS 1m"] / 1000000, 2)
|
||||||
|
setattr(data, board_map[_id], hashrate)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def get_mac(self):
|
async def get_mac(self):
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from pyasic.miners import BaseMiner
|
|||||||
from pyasic.API import APIError
|
from pyasic.API import APIError
|
||||||
|
|
||||||
from pyasic.data import MinerData
|
from pyasic.data import MinerData
|
||||||
|
from pyasic.data.error_codes import WhatsminerError
|
||||||
|
|
||||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||||
|
|
||||||
@@ -144,12 +145,16 @@ class BTMiner(BaseMiner):
|
|||||||
summary_data = summary.get("SUMMARY")
|
summary_data = summary.get("SUMMARY")
|
||||||
if summary_data:
|
if summary_data:
|
||||||
if len(summary_data) > 0:
|
if len(summary_data) > 0:
|
||||||
|
wattage_limit = None
|
||||||
if summary_data[0].get("MAC"):
|
if summary_data[0].get("MAC"):
|
||||||
mac = summary_data[0]["MAC"]
|
mac = summary_data[0]["MAC"]
|
||||||
|
|
||||||
if summary_data[0].get("Env Temp"):
|
if summary_data[0].get("Env Temp"):
|
||||||
data.env_temp = summary_data[0]["Env Temp"]
|
data.env_temp = summary_data[0]["Env Temp"]
|
||||||
|
|
||||||
|
if summary_data[0].get("Power Limit"):
|
||||||
|
wattage_limit = summary_data[0]["Power Limit"]
|
||||||
|
|
||||||
data.fan_1 = summary_data[0]["Fan Speed In"]
|
data.fan_1 = summary_data[0]["Fan Speed In"]
|
||||||
data.fan_2 = summary_data[0]["Fan Speed Out"]
|
data.fan_2 = summary_data[0]["Fan Speed Out"]
|
||||||
|
|
||||||
@@ -160,7 +165,20 @@ class BTMiner(BaseMiner):
|
|||||||
wattage = summary_data[0].get("Power")
|
wattage = summary_data[0].get("Power")
|
||||||
if wattage:
|
if wattage:
|
||||||
data.wattage = round(wattage)
|
data.wattage = round(wattage)
|
||||||
data.wattage_limit = round(wattage)
|
|
||||||
|
if not wattage_limit:
|
||||||
|
wattage_limit = round(wattage)
|
||||||
|
|
||||||
|
data.wattage_limit = wattage_limit
|
||||||
|
|
||||||
|
if summary_data[0].get("Error Code Count"):
|
||||||
|
for i in range(summary_data[0]["Error Code Count"]):
|
||||||
|
if summary_data[0].get(f"Error Code {i}"):
|
||||||
|
data.errors.append(
|
||||||
|
WhatsminerError(
|
||||||
|
error_code=summary_data[0][f"Error Code {i}"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if devs:
|
if devs:
|
||||||
temp_data = devs.get("DEVS")
|
temp_data = devs.get("DEVS")
|
||||||
@@ -170,8 +188,10 @@ class BTMiner(BaseMiner):
|
|||||||
_id = board["ASC"]
|
_id = board["ASC"]
|
||||||
chip_temp = round(board["Chip Temp Avg"])
|
chip_temp = round(board["Chip Temp Avg"])
|
||||||
board_temp = round(board["Temperature"])
|
board_temp = round(board["Temperature"])
|
||||||
|
hashrate = round(board["MHS 1m"] / 1000000, 2)
|
||||||
setattr(data, f"{board_map[_id]}_chip_temp", chip_temp)
|
setattr(data, f"{board_map[_id]}_chip_temp", chip_temp)
|
||||||
setattr(data, f"{board_map[_id]}_temp", board_temp)
|
setattr(data, f"{board_map[_id]}_temp", board_temp)
|
||||||
|
setattr(data, f"{board_map[_id]}_hashrate", hashrate)
|
||||||
|
|
||||||
if devs:
|
if devs:
|
||||||
boards = devs.get("DEVS")
|
boards = devs.get("DEVS")
|
||||||
|
|||||||
@@ -111,6 +111,9 @@ class CGMiner(BaseMiner):
|
|||||||
async def get_data(self):
|
async def get_data(self):
|
||||||
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
||||||
|
|
||||||
|
board_offset = -1
|
||||||
|
fan_offset = -1
|
||||||
|
|
||||||
model = await self.get_model()
|
model = await self.get_model()
|
||||||
hostname = await self.get_hostname()
|
hostname = await self.get_hostname()
|
||||||
mac = await self.get_mac()
|
mac = await self.get_mac()
|
||||||
@@ -126,7 +129,9 @@ class CGMiner(BaseMiner):
|
|||||||
|
|
||||||
miner_data = None
|
miner_data = None
|
||||||
for i in range(DATA_RETRIES):
|
for i in range(DATA_RETRIES):
|
||||||
miner_data = await self.api.multicommand("summary", "pools", "stats")
|
miner_data = await self.api.multicommand(
|
||||||
|
"summary", "pools", "stats", ignore_x19_error=True
|
||||||
|
)
|
||||||
if miner_data:
|
if miner_data:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -141,25 +146,65 @@ class CGMiner(BaseMiner):
|
|||||||
hr = summary.get("SUMMARY")
|
hr = summary.get("SUMMARY")
|
||||||
if hr:
|
if hr:
|
||||||
if len(hr) > 0:
|
if len(hr) > 0:
|
||||||
hr = hr[0].get("GHS 1m")
|
hr = hr[0].get("GHS av")
|
||||||
if hr:
|
if hr:
|
||||||
data.hashrate = round(hr / 1000, 2)
|
data.hashrate = round(hr / 1000, 2)
|
||||||
|
|
||||||
|
if stats:
|
||||||
|
boards = stats.get("STATS")
|
||||||
|
if boards:
|
||||||
|
if len(boards) > 0:
|
||||||
|
for board_num in range(1, 16, 5):
|
||||||
|
for _b_num in range(5):
|
||||||
|
b = boards[1].get(f"chain_acn{board_num + _b_num}")
|
||||||
|
|
||||||
|
if b and not b == 0 and board_offset == -1:
|
||||||
|
board_offset = board_num
|
||||||
|
if board_offset == -1:
|
||||||
|
board_offset = 1
|
||||||
|
|
||||||
|
data.left_chips = boards[1].get(f"chain_acn{board_offset}")
|
||||||
|
data.center_chips = boards[1].get(f"chain_acn{board_offset+1}")
|
||||||
|
data.right_chips = boards[1].get(f"chain_acn{board_offset+2}")
|
||||||
|
|
||||||
|
data.left_board_hashrate = round(
|
||||||
|
float(boards[1].get(f"chain_rate{board_offset}")) / 1000, 2
|
||||||
|
)
|
||||||
|
data.center_board_hashrate = round(
|
||||||
|
float(boards[1].get(f"chain_rate{board_offset+1}")) / 1000, 2
|
||||||
|
)
|
||||||
|
data.right_board_hashrate = round(
|
||||||
|
float(boards[1].get(f"chain_rate{board_offset+2}")) / 1000, 2
|
||||||
|
)
|
||||||
|
|
||||||
if stats:
|
if stats:
|
||||||
temp = stats.get("STATS")
|
temp = stats.get("STATS")
|
||||||
if temp:
|
if temp:
|
||||||
if len(temp) > 1:
|
if len(temp) > 1:
|
||||||
data.fan_1 = temp[1].get("fan1")
|
for fan_num in range(1, 8, 4):
|
||||||
data.fan_2 = temp[1].get("fan2")
|
for _f_num in range(4):
|
||||||
data.fan_3 = temp[1].get("fan3")
|
f = temp[1].get(f"fan{fan_num + _f_num}")
|
||||||
data.fan_4 = temp[1].get("fan4")
|
if f and not f == 0 and fan_offset == -1:
|
||||||
|
fan_offset = fan_num
|
||||||
|
if fan_offset == -1:
|
||||||
|
fan_offset = 1
|
||||||
|
for fan in range(self.fan_count):
|
||||||
|
setattr(
|
||||||
|
data, f"fan_{fan + 1}", temp[1].get(f"fan{fan_offset+fan}")
|
||||||
|
)
|
||||||
|
|
||||||
board_map = {1: "left_board", 2: "center_board", 3: "right_board"}
|
board_map = {0: "left_board", 1: "center_board", 2: "right_board"}
|
||||||
for item in range(1, 4):
|
env_temp_list = []
|
||||||
board_temp = temp[1].get(f"temp{item}")
|
for item in range(3):
|
||||||
chip_temp = temp[1].get(f"temp2_{item}")
|
board_temp = temp[1].get(f"temp{item + board_offset}")
|
||||||
|
chip_temp = temp[1].get(f"temp2_{item + board_offset}")
|
||||||
setattr(data, f"{board_map[item]}_chip_temp", chip_temp)
|
setattr(data, f"{board_map[item]}_chip_temp", chip_temp)
|
||||||
setattr(data, f"{board_map[item]}_temp", board_temp)
|
setattr(data, f"{board_map[item]}_temp", board_temp)
|
||||||
|
if f"temp_pcb{item}" in temp[1].keys():
|
||||||
|
env_temp = temp[1][f"temp_pcb{item}"].split("-")[0]
|
||||||
|
if not env_temp == 0:
|
||||||
|
env_temp_list.append(int(env_temp))
|
||||||
|
data.env_temp = sum(env_temp_list) / len(env_temp_list)
|
||||||
|
|
||||||
if pools:
|
if pools:
|
||||||
pool_1 = None
|
pool_1 = None
|
||||||
@@ -173,16 +218,19 @@ class CGMiner(BaseMiner):
|
|||||||
if not pool_1_user:
|
if not pool_1_user:
|
||||||
pool_1_user = pool.get("User")
|
pool_1_user = pool.get("User")
|
||||||
pool_1 = pool["URL"]
|
pool_1 = pool["URL"]
|
||||||
pool_1_quota = pool["Quota"]
|
if pool.get("Quota"):
|
||||||
|
pool_2_quota = pool.get("Quota")
|
||||||
elif not pool_2_user:
|
elif not pool_2_user:
|
||||||
pool_2_user = pool.get("User")
|
pool_2_user = pool.get("User")
|
||||||
pool_2 = pool["URL"]
|
pool_2 = pool["URL"]
|
||||||
pool_2_quota = pool["Quota"]
|
if pool.get("Quota"):
|
||||||
|
pool_2_quota = pool.get("Quota")
|
||||||
if not pool.get("User") == pool_1_user:
|
if not pool.get("User") == pool_1_user:
|
||||||
if not pool_2_user == pool.get("User"):
|
if not pool_2_user == pool.get("User"):
|
||||||
pool_2_user = pool.get("User")
|
pool_2_user = pool.get("User")
|
||||||
pool_2 = pool["URL"]
|
pool_2 = pool["URL"]
|
||||||
pool_2_quota = pool["Quota"]
|
if pool.get("Quota"):
|
||||||
|
pool_2_quota = pool.get("Quota")
|
||||||
if pool_2_user and not pool_2_user == pool_1_user:
|
if pool_2_user and not pool_2_user == pool_1_user:
|
||||||
quota = f"{pool_1_quota}/{pool_2_quota}"
|
quota = f"{pool_1_quota}/{pool_2_quota}"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import TypeVar, Tuple, List
|
from typing import TypeVar, Tuple, List, Union
|
||||||
from collections.abc import AsyncIterable
|
from collections.abc import AsyncIterable
|
||||||
from pyasic.miners import BaseMiner
|
from pyasic.miners import BaseMiner
|
||||||
|
|
||||||
@@ -10,7 +10,9 @@ from pyasic.miners._backends.cgminer import CGMiner # noqa - Ignore _module imp
|
|||||||
from pyasic.miners._backends.bmminer import BMMiner # noqa - Ignore _module import
|
from pyasic.miners._backends.bmminer import BMMiner # noqa - Ignore _module import
|
||||||
from pyasic.miners._backends.bosminer import BOSMiner # noqa - Ignore _module import
|
from pyasic.miners._backends.bosminer import BOSMiner # noqa - Ignore _module import
|
||||||
from pyasic.miners._backends.btminer import BTMiner # noqa - Ignore _module import
|
from pyasic.miners._backends.btminer import BTMiner # noqa - Ignore _module import
|
||||||
from pyasic.miners._backends.bosminer_old import BOSMinerOld # noqa - Ignore _module import
|
from pyasic.miners._backends.bosminer_old import ( # noqa - Ignore _module import
|
||||||
|
BOSMinerOld,
|
||||||
|
)
|
||||||
|
|
||||||
from pyasic.miners.unknown import UnknownMiner
|
from pyasic.miners.unknown import UnknownMiner
|
||||||
|
|
||||||
@@ -227,11 +229,13 @@ class Singleton(type):
|
|||||||
|
|
||||||
|
|
||||||
class MinerFactory(metaclass=Singleton):
|
class MinerFactory(metaclass=Singleton):
|
||||||
|
"""A factory to handle identification and selection of the proper class of miner"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.miners = {}
|
self.miners = {}
|
||||||
|
|
||||||
async def get_miner_generator(
|
async def get_miner_generator(
|
||||||
self, ips: List[ipaddress.ip_address or str]
|
self, ips: List[Union[ipaddress.ip_address, str]]
|
||||||
) -> AsyncIterable[AnyMiner]:
|
) -> AsyncIterable[AnyMiner]:
|
||||||
"""
|
"""
|
||||||
Get Miner objects from ip addresses using an async generator.
|
Get Miner objects from ip addresses using an async generator.
|
||||||
@@ -240,6 +244,9 @@ class MinerFactory(metaclass=Singleton):
|
|||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
ips: a list of ip addresses to get miners for.
|
ips: a list of ip addresses to get miners for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An async iterable containing miners.
|
||||||
"""
|
"""
|
||||||
# get the event loop
|
# get the event loop
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
@@ -254,8 +261,15 @@ class MinerFactory(metaclass=Singleton):
|
|||||||
for miner in scanned:
|
for miner in scanned:
|
||||||
yield await miner
|
yield await miner
|
||||||
|
|
||||||
async def get_miner(self, ip: ipaddress.ip_address or str) -> AnyMiner:
|
async def get_miner(self, ip: Union[ipaddress.ip_address, str]) -> AnyMiner:
|
||||||
"""Decide a miner type using the IP address of the miner."""
|
"""Decide a miner type using the IP address of the miner.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
ip: An `ipaddress.ip_address` or string of the IP to find the miner.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A miner class.
|
||||||
|
"""
|
||||||
if isinstance(ip, str):
|
if isinstance(ip, str):
|
||||||
ip = ipaddress.ip_address(ip)
|
ip = ipaddress.ip_address(ip)
|
||||||
# check if the miner already exists in cache
|
# check if the miner already exists in cache
|
||||||
@@ -474,17 +488,20 @@ class MinerFactory(metaclass=Singleton):
|
|||||||
|
|
||||||
# final try on a braiins OS bug with devdetails not returning
|
# final try on a braiins OS bug with devdetails not returning
|
||||||
else:
|
else:
|
||||||
async with asyncssh.connect(
|
try:
|
||||||
str(ip),
|
async with asyncssh.connect(
|
||||||
known_hosts=None,
|
str(ip),
|
||||||
username="root",
|
known_hosts=None,
|
||||||
password="admin",
|
username="root",
|
||||||
server_host_key_algs=["ssh-rsa"],
|
password="admin",
|
||||||
) as conn:
|
server_host_key_algs=["ssh-rsa"],
|
||||||
cfg = await conn.run("bosminer config --data")
|
) as conn:
|
||||||
if cfg:
|
cfg = await conn.run("bosminer config --data")
|
||||||
cfg = json.loads(cfg.stdout)
|
if cfg:
|
||||||
model = cfg.get("data").get("format").get("model")
|
cfg = json.loads(cfg.stdout)
|
||||||
|
model = cfg.get("data").get("format").get("model")
|
||||||
|
except asyncssh.misc.PermissionDenied:
|
||||||
|
pass
|
||||||
|
|
||||||
if model:
|
if model:
|
||||||
# whatsminer have a V in their version string (M20SV41), remove everything after it
|
# whatsminer have a V in their version string (M20SV41), remove everything after it
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from pyasic.network.net_range import MinerNetworkRange
|
from pyasic.network.net_range import MinerNetworkRange
|
||||||
from pyasic.miners.miner_factory import MinerFactory
|
from pyasic.miners.miner_factory import MinerFactory
|
||||||
@@ -12,12 +13,27 @@ from pyasic.settings import (
|
|||||||
|
|
||||||
|
|
||||||
class MinerNetwork:
|
class MinerNetwork:
|
||||||
|
"""A class to handle a network containing miners. Handles scanning and gets miners via [`MinerFactory`][pyasic.miners.miner_factory.MinerFactory].
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
ip_addr: ### An IP address, range of IP addresses, or a list of IPs
|
||||||
|
* Takes a single IP address as an `ipadddress.ipaddress()` or a string
|
||||||
|
* Takes a string formatted as:
|
||||||
|
```f"{ip_range_1_start}-{ip_range_1_end}, {ip_address_1}, {ip_range_2_start}-{ip_range_2_end}, {ip_address_2}..."```
|
||||||
|
* Also takes a list of strings or `ipaddress.ipaddress` formatted as:
|
||||||
|
```[{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]```
|
||||||
|
mask: A subnet mask to use when constructing the network. Only used if `ip_addr` is a single IP.
|
||||||
|
Defaults to /24 (255.255.255.0 or 0.0.0.255)
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, ip_addr: str or None = None, mask: str or int or None = None
|
self, ip_addr: Union[str, None] = None, mask: Union[str, int, None] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
self.network = None
|
self.network = None
|
||||||
self.ip_addr = ip_addr
|
self.ip_addr = ip_addr
|
||||||
self.connected_miners = {}
|
self.connected_miners = {}
|
||||||
|
if mask.startswith("/"):
|
||||||
|
mask = mask.replace("/", "")
|
||||||
self.mask = mask
|
self.mask = mask
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import ipaddress
|
|||||||
class MinerNetworkRange:
|
class MinerNetworkRange:
|
||||||
"""A MinerNetwork that takes a range of IP addresses.
|
"""A MinerNetwork that takes a range of IP addresses.
|
||||||
|
|
||||||
:param ip_range: A range of IP addresses to put in the network, or a list of IPs
|
Parameters:
|
||||||
Takes a string formatted as:
|
ip_range: ## A range of IP addresses to put in the network, or a list of IPs
|
||||||
{ip_range_1_start}-{ip_range_1_end}, {ip_address_1},
|
* Takes a string formatted as:
|
||||||
{ip_range_2_start}-{ip_range_2_end}, {ip_address_2}...
|
* {ip_range_1_start}-{ip_range_1_end}, {ip_address_1}, {ip_range_2_start}-{ip_range_2_end}, {ip_address_2}...
|
||||||
Also takes a list of strings formatted as:
|
* Also takes a list of strings formatted as:
|
||||||
[{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]
|
* [{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip_range: Union[str, list]):
|
def __init__(self, ip_range: Union[str, list]):
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pyasic"
|
name = "pyasic"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
|
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
|
||||||
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||||
|
repository = "https://github.com/UpstreamData/pyasic"
|
||||||
|
documentation = "https://pyasic.readthedocs.io/en/latest/"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
python = "^3.9"
|
||||||
|
|||||||
Reference in New Issue
Block a user