Compare commits
3 Commits
v0.72.5
...
dev_scan_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54f0292712 | ||
|
|
46c56134f7 | ||
|
|
1c1f7f1098 |
6
.github/workflows/python-publish.yml
vendored
6
.github/workflows/python-publish.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v2
|
||||||
- name: Publish GH release
|
- name: Publish GH release
|
||||||
uses: softprops/action-gh-release@v2.1.0
|
uses: softprops/action-gh-release@v0.1.14
|
||||||
- name: Build using poetry and publish to PyPi
|
- name: Build using poetry and publish to PyPi
|
||||||
uses: JRubics/poetry-publish@v2.0
|
uses: JRubics/poetry-publish@v1.11
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
||||||
|
|||||||
@@ -1,33 +1,16 @@
|
|||||||
ci:
|
|
||||||
skip:
|
|
||||||
- poetry-lock
|
|
||||||
- unittest
|
|
||||||
- generate-docs
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/python-poetry/poetry
|
|
||||||
rev: 2.1.1
|
|
||||||
hooks:
|
|
||||||
- id: poetry-check
|
|
||||||
- id: poetry-lock
|
|
||||||
- id: poetry-install
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v5.0.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
name: check-yaml for mkdocs.yml
|
|
||||||
files: ^mkdocs\.yml$
|
|
||||||
args: [--unsafe]
|
|
||||||
- id: check-yaml
|
|
||||||
name: check-yaml for other YAML files
|
|
||||||
exclude: ^mkdocs\.yml$
|
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 25.1.0
|
rev: 24.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/pycqa/isort
|
||||||
rev: 6.0.1
|
rev: 5.13.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
name: isort (python)
|
name: isort (python)
|
||||||
@@ -38,12 +21,7 @@ repos:
|
|||||||
name: unittest
|
name: unittest
|
||||||
entry: python -m unittest discover
|
entry: python -m unittest discover
|
||||||
language: system
|
language: system
|
||||||
types: [ python ]
|
'types': [python]
|
||||||
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
|
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
- id: generate-docs
|
stages: [commit]
|
||||||
name: generate-docs
|
|
||||||
entry: python docs/generate_miners.py
|
|
||||||
language: system
|
|
||||||
types: [ python ]
|
|
||||||
pass_filenames: false
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
# .readthedocs.yaml
|
# .readthedocs.yaml
|
||||||
|
# Read the Docs configuration file
|
||||||
|
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||||
|
|
||||||
|
# Required
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
# Set the version of Python and other tools you might need
|
# Set the version of Python and other tools you might need
|
||||||
build:
|
build:
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
tools: { python: "3.11" }
|
tools:
|
||||||
jobs:
|
python: "3.9"
|
||||||
pre_create_environment:
|
|
||||||
- asdf plugin add poetry
|
|
||||||
- asdf install poetry latest
|
|
||||||
- asdf global poetry latest
|
|
||||||
- poetry config virtualenvs.create false
|
|
||||||
post_install:
|
|
||||||
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --only docs
|
|
||||||
|
|
||||||
mkdocs:
|
mkdocs:
|
||||||
configuration: mkdocs.yml
|
configuration: mkdocs.yml
|
||||||
|
|
||||||
|
# Optionally declare the Python requirements required to build your docs
|
||||||
|
python:
|
||||||
|
install:
|
||||||
|
- requirements: docs/requirements.txt
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -42,7 +42,7 @@ It is recommended to install `pyasic` in a [virtual environment](https://realpyt
|
|||||||
|
|
||||||
##### Installing `pyasic`
|
##### Installing `pyasic`
|
||||||
|
|
||||||
`python -m pip install pyasic` or `poetry install`
|
`python -m pip install .` or `poetry install`
|
||||||
|
|
||||||
##### Additional Developer Setup
|
##### Additional Developer Setup
|
||||||
```
|
```
|
||||||
@@ -50,13 +50,6 @@ poetry install --with dev
|
|||||||
pre-commit install
|
pre-commit install
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Building Documentation Locally
|
|
||||||
```
|
|
||||||
poetry install --with docs
|
|
||||||
python docs/generate_miners.py
|
|
||||||
poetry run mkdocs serve
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
@@ -262,30 +255,24 @@ if __name__ == "__main__":
|
|||||||
```python
|
```python
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
|
|
||||||
settings.update("default_antminer_web_password", "my_pwd")
|
settings.update("default_antminer_password", "my_pwd")
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Default values:
|
##### Default values:
|
||||||
```
|
```
|
||||||
"network_ping_retries": 1,
|
"network_ping_retries": 1,
|
||||||
"network_ping_timeout": 3,
|
"network_ping_timeout": 3,
|
||||||
"network_scan_semaphore": None,
|
"network_scan_threads": 300,
|
||||||
"factory_get_retries": 1,
|
"factory_get_retries": 1,
|
||||||
"factory_get_timeout": 3,
|
"factory_get_timeout": 3,
|
||||||
"get_data_retries": 1,
|
"get_data_retries": 1,
|
||||||
"api_function_timeout": 5,
|
"api_function_timeout": 5,
|
||||||
"antminer_mining_mode_as_str": False,
|
"default_whatsminer_password": "admin",
|
||||||
"default_whatsminer_rpc_password": "admin",
|
"default_innosilicon_password": "admin",
|
||||||
"default_innosilicon_web_password": "admin",
|
"default_antminer_password": "root",
|
||||||
"default_antminer_web_password": "root",
|
"default_bosminer_password": "root",
|
||||||
"default_bosminer_web_password": "root",
|
"default_vnish_password": "admin",
|
||||||
"default_vnish_web_password": "admin",
|
"default_goldshell_password": "123456789",
|
||||||
"default_goldshell_web_password": "123456789",
|
|
||||||
"default_auradine_web_password": "admin",
|
|
||||||
"default_epic_web_password": "letmein",
|
|
||||||
"default_hive_web_password": "admin",
|
|
||||||
"default_antminer_ssh_password": "miner",
|
|
||||||
"default_bosminer_ssh_password": "root",
|
|
||||||
|
|
||||||
# ADVANCED
|
# ADVANCED
|
||||||
# Only use this if you know what you are doing
|
# Only use this if you know what you are doing
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import asyncio
|
|||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from pyasic.miners.factory import MINER_CLASSES, MinerTypes
|
from pyasic.miners.factory import MINER_CLASSES, MinerTypes
|
||||||
|
|
||||||
@@ -50,21 +49,6 @@ def backend_str(backend: MinerTypes) -> str:
|
|||||||
return "LuxOS Firmware Miners"
|
return "LuxOS Firmware Miners"
|
||||||
case MinerTypes.MARATHON:
|
case MinerTypes.MARATHON:
|
||||||
return "Mara Firmware Miners"
|
return "Mara Firmware Miners"
|
||||||
case MinerTypes.BITAXE:
|
|
||||||
return "Stock Firmware BitAxe Miners"
|
|
||||||
case MinerTypes.LUCKYMINER:
|
|
||||||
return "Stock Firmware Lucky Miners"
|
|
||||||
case MinerTypes.ICERIVER:
|
|
||||||
return "Stock Firmware IceRiver Miners"
|
|
||||||
case MinerTypes.HAMMER:
|
|
||||||
return "Stock Firmware Hammer Miners"
|
|
||||||
case MinerTypes.VOLCMINER:
|
|
||||||
return "Stock Firmware Volcminers"
|
|
||||||
case MinerTypes.ELPHAPEX:
|
|
||||||
return "Stock Firmware Elphapex Miners"
|
|
||||||
case MinerTypes.MSKMINER:
|
|
||||||
return "MSKMiner Firmware Miners"
|
|
||||||
raise TypeError("Unknown miner backend, cannot generate docs")
|
|
||||||
|
|
||||||
|
|
||||||
def create_url_str(mtype: str):
|
def create_url_str(mtype: str):
|
||||||
@@ -79,17 +63,11 @@ def create_url_str(mtype: str):
|
|||||||
|
|
||||||
HEADER_FORMAT = "# pyasic\n## {} Models\n\n"
|
HEADER_FORMAT = "# pyasic\n## {} Models\n\n"
|
||||||
MINER_HEADER_FORMAT = "## {}\n"
|
MINER_HEADER_FORMAT = "## {}\n"
|
||||||
DATA_FORMAT = """
|
DATA_FORMAT = """::: {}
|
||||||
- [{}] Shutdowns
|
|
||||||
- [{}] Power Modes
|
|
||||||
- [{}] Setpoints
|
|
||||||
- [{}] Presets
|
|
||||||
|
|
||||||
::: {}
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
"""
|
"""
|
||||||
SUPPORTED_TYPES_HEADER = """# pyasic
|
SUPPORTED_TYPES_HEADER = """# pyasic
|
||||||
@@ -97,8 +75,6 @@ SUPPORTED_TYPES_HEADER = """# pyasic
|
|||||||
|
|
||||||
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
||||||
|
|
||||||
Keep in mind that some functionality is only supported for specific miners or firmwares, please check the page for your miner to make sure the functionality you need is supported.
|
|
||||||
|
|
||||||
##### pyasic currently supports the following miners and subtypes:
|
##### pyasic currently supports the following miners and subtypes:
|
||||||
<style>
|
<style>
|
||||||
details {
|
details {
|
||||||
@@ -129,49 +105,39 @@ BACKEND_TYPE_CLOSER = """
|
|||||||
</details>"""
|
</details>"""
|
||||||
|
|
||||||
m_data = {}
|
m_data = {}
|
||||||
done = []
|
|
||||||
|
|
||||||
for m in MINER_CLASSES:
|
for m in MINER_CLASSES:
|
||||||
for t in sorted(MINER_CLASSES[m], key=lambda x: x or ""):
|
for t in MINER_CLASSES[m]:
|
||||||
if t is not None and MINER_CLASSES[m][t] not in done:
|
if t is not None:
|
||||||
miner = MINER_CLASSES[m][t]
|
miner = MINER_CLASSES[m][t]
|
||||||
if make(miner) not in m_data:
|
if make(miner) not in m_data:
|
||||||
m_data[make(miner)] = {}
|
m_data[make(miner)] = {}
|
||||||
if model_type(miner) not in m_data[make(miner)]:
|
if model_type(miner) not in m_data[make(miner)]:
|
||||||
m_data[make(miner)][model_type(miner)] = []
|
m_data[make(miner)][model_type(miner)] = []
|
||||||
m_data[make(miner)][model_type(miner)].append(miner)
|
m_data[make(miner)][model_type(miner)].append(miner)
|
||||||
done.append(miner)
|
|
||||||
|
|
||||||
|
|
||||||
def create_directory_structure(directory, data):
|
async def create_directory_structure(directory, data):
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
|
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
subdirectory = os.path.join(directory, key)
|
subdirectory = os.path.join(directory, key)
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
create_directory_structure(subdirectory, value)
|
await create_directory_structure(subdirectory, value)
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
file_path = os.path.join(subdirectory + ".md")
|
file_path = os.path.join(subdirectory + ".md")
|
||||||
|
|
||||||
with open(file_path, "w") as file:
|
with open(file_path, "w") as file:
|
||||||
file.write(HEADER_FORMAT.format(key))
|
file.write(HEADER_FORMAT.format(key))
|
||||||
for item in value:
|
for item in value:
|
||||||
obj = item("1.1.1.1")
|
header = await item("1.1.1.1").get_model()
|
||||||
header = obj.model
|
|
||||||
file.write(MINER_HEADER_FORMAT.format(header))
|
file.write(MINER_HEADER_FORMAT.format(header))
|
||||||
file.write(
|
file.write(DATA_FORMAT.format(path(item)))
|
||||||
DATA_FORMAT.format(
|
|
||||||
"x" if obj.supports_shutdown else " ",
|
|
||||||
"x" if obj.supports_power_modes else " ",
|
|
||||||
"x" if obj.supports_autotuning else " ",
|
|
||||||
"x" if obj.supports_presets else " ",
|
|
||||||
path(item),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_supported_types(directory):
|
async def create_supported_types(directory):
|
||||||
with open(os.path.join(directory, "supported_types.md"), "w") as file:
|
with open(os.path.join(directory, "supported_types.md"), "w") as file:
|
||||||
file.write(SUPPORTED_TYPES_HEADER)
|
file.write(SUPPORTED_TYPES_HEADER)
|
||||||
for mback in MINER_CLASSES:
|
for mback in MINER_CLASSES:
|
||||||
@@ -188,7 +154,7 @@ def create_supported_types(directory):
|
|||||||
for mtype in backend_types:
|
for mtype in backend_types:
|
||||||
file.write(MINER_TYPE_HEADER.format(mtype))
|
file.write(MINER_TYPE_HEADER.format(mtype))
|
||||||
for minstance in backend_types[mtype]:
|
for minstance in backend_types[mtype]:
|
||||||
model = minstance("1.1.1.1").model
|
model = await minstance("1.1.1.1").get_model()
|
||||||
file.write(
|
file.write(
|
||||||
MINER_DETAILS.format(
|
MINER_DETAILS.format(
|
||||||
make(minstance), mtype, create_url_str(model), model
|
make(minstance), mtype, create_url_str(model), model
|
||||||
@@ -198,7 +164,6 @@ def create_supported_types(directory):
|
|||||||
file.write(BACKEND_TYPE_CLOSER)
|
file.write(BACKEND_TYPE_CLOSER)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
root_directory = os.path.join(os.getcwd(), "miners")
|
||||||
root_directory = Path(__file__).parent.joinpath("miners")
|
asyncio.run(create_directory_structure(root_directory, m_data))
|
||||||
create_directory_structure(root_directory, m_data)
|
asyncio.run(create_supported_types(root_directory))
|
||||||
create_supported_types(root_directory)
|
|
||||||
|
|||||||
210
docs/index.md
210
docs/index.md
@@ -11,149 +11,145 @@
|
|||||||
[](https://pyasic.readthedocs.io/en/latest/)
|
[](https://pyasic.readthedocs.io/en/latest/)
|
||||||
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
|
---
|
||||||
## Intro
|
## Intro
|
||||||
---
|
---
|
||||||
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
||||||
|
|
||||||
[Click here to view supported miner types](miners/supported_types.md)
|
[Click here to view supported miner types](miners/supported_types.md)
|
||||||
|
|
||||||
## Installation
|
|
||||||
---
|
---
|
||||||
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system.
|
## Installation
|
||||||
`pyasic` can be installed directly from pip, either with `pip install pyasic`, or a different command if using a tool like `pypoetry`.
|
|
||||||
|
|
||||||
|
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include:
|
||||||
|
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default
|
||||||
|
|
||||||
|
```
|
||||||
|
poetry install
|
||||||
|
```
|
||||||
|
|
||||||
|
- [venv](https://docs.python.org/3/library/venv.html): included in Python standard library but has fewer features than other options
|
||||||
|
- [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv): [pyenv](https://github.com/pyenv/pyenv) plugin for managing virtualenvs
|
||||||
|
|
||||||
|
```
|
||||||
|
pyenv install <python version number>
|
||||||
|
pyenv virtualenv <python version number> <env name>
|
||||||
|
pyenv activate <env name>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [conda](https://docs.conda.io/en/latest/)
|
||||||
|
|
||||||
|
##### Installing `pyasic`
|
||||||
|
|
||||||
|
`python -m pip install .` or `poetry install`
|
||||||
|
|
||||||
|
---
|
||||||
## Getting started
|
## Getting started
|
||||||
---
|
---
|
||||||
Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
|
Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
|
||||||
|
|
||||||
### Scanning for miners
|
##### Scanning for miners
|
||||||
To scan for miners in `pyasic`, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
|
To scan for miners in `pyasic`, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
|
||||||
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
|
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
|
||||||
```python3
|
```python
|
||||||
import asyncio# (1)!
|
import asyncio # asyncio for handling the async part
|
||||||
from pyasic.network import MinerNetwork# (2)!
|
from pyasic.network import MinerNetwork # miner network handles the scanning
|
||||||
|
|
||||||
|
|
||||||
async def scan_miners():# (3)!
|
async def scan_miners(): # define async scan function to allow awaiting
|
||||||
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
# create a miner network
|
||||||
|
# you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
||||||
|
network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
|
||||||
|
|
||||||
miners = await network.scan()# (5)!
|
# scan for miners asynchronously
|
||||||
|
# this will return the correct type of miners if they are supported with all functionality.
|
||||||
|
miners = await network.scan()
|
||||||
print(miners)
|
print(miners)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(scan_miners())# (6)!
|
asyncio.run(scan_miners()) # run the scan asynchronously with asyncio.run()
|
||||||
```
|
```
|
||||||
|
|
||||||
1. `asyncio` for handling the async part.
|
|
||||||
2. `MinerNetwork` handles the scanning.
|
|
||||||
3. Define an async function to allow awaiting.
|
|
||||||
4. Create a miner network.
|
|
||||||
You can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
|
||||||
This uses the 192.168.1.0-255 network.
|
|
||||||
5. Scan for miners asynchronously.
|
|
||||||
This will return the correct type of miners (if they are supported) with all functionality.
|
|
||||||
6. Run the scan asynchronously with asyncio.run().
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### Creating miners based on IP
|
##### Creating miners based on IP
|
||||||
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner].
|
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner].
|
||||||
The function [`get_miner()`][pyasic.miners.get_miner] will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner.
|
The function [`get_miner()`][pyasic.miners.get_miner] will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner.
|
||||||
```python
|
```python
|
||||||
import asyncio# (1)!
|
import asyncio # asyncio for handling the async part
|
||||||
from pyasic import get_miner# (2)!
|
from pyasic import get_miner # handles miner creation
|
||||||
|
|
||||||
|
|
||||||
async def get_miners():# (3)!
|
async def get_miners(): # define async scan function to allow awaiting
|
||||||
miner_1 = await get_miner("192.168.1.75")# (4)!
|
# get the miner with the miner factory
|
||||||
|
# the miner factory is a singleton, and will always use the same object and cache
|
||||||
|
# this means you can always call it as MinerFactory().get_miner(), or just get_miner()
|
||||||
|
miner_1 = await get_miner("192.168.1.75")
|
||||||
miner_2 = await get_miner("192.168.1.76")
|
miner_2 = await get_miner("192.168.1.76")
|
||||||
print(miner_1, miner_2)
|
print(miner_1, miner_2)
|
||||||
|
|
||||||
|
# can also gather these, since they are async
|
||||||
|
# gathering them will get them both at the same time
|
||||||
|
# this makes it much faster to get a lot of miners at a time
|
||||||
tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")]
|
tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")]
|
||||||
miners = await asyncio.gather(*tasks)# (5)!
|
miners = await asyncio.gather(*tasks)
|
||||||
print(miners)
|
print(miners)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(get_miners())# (6)!
|
asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run()
|
||||||
```
|
```
|
||||||
|
|
||||||
1. `asyncio` for handling the async part.
|
---
|
||||||
2. `get_miner` handles the miner type selection.
|
|
||||||
3. Define an async function to allow awaiting.
|
|
||||||
4. Get the miner.
|
|
||||||
5. Can also gather these, since they are async.
|
|
||||||
Gathering them will get them both at the same time.
|
|
||||||
This makes it much faster to get a lot of miners at a time.
|
|
||||||
6. Get the miners asynchronously with asyncio.run().
|
|
||||||
|
|
||||||
## Data gathering
|
## Data gathering
|
||||||
---
|
---
|
||||||
Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`.
|
Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`.
|
||||||
This function will return an instance of the dataclass [`MinerData`][pyasic.data.MinerData] with all data it can gather from the miner.
|
This function will return an instance of the dataclass [`MinerData`][pyasic.data.MinerData] with all data it can gather from the miner.
|
||||||
Each piece of data in a [`MinerData`][pyasic.data.MinerData] instance can be referenced by getting it as an attribute, such as [`MinerData().hashrate`][pyasic.data.MinerData].
|
Each piece of data in a [`MinerData`][pyasic.data.MinerData] instance can be referenced by getting it as an attribute, such as [`MinerData().hashrate`][pyasic.data.MinerData].
|
||||||
|
|
||||||
### One miner
|
##### One miner
|
||||||
```python
|
```python
|
||||||
import asyncio# (1)!
|
import asyncio
|
||||||
from pyasic import get_miner# (2)!
|
from pyasic import get_miner
|
||||||
|
|
||||||
|
async def gather_miner_data():
|
||||||
async def gather_miner_data():# (3)!
|
miner = await get_miner("192.168.1.75")
|
||||||
miner = await get_miner("192.168.1.75")# (4)!
|
if miner is not None:
|
||||||
if miner is not None:# (5)!
|
miner_data = await miner.get_data()
|
||||||
miner_data = await miner.get_data()# (6)!
|
print(miner_data) # all data from the dataclass
|
||||||
print(miner_data)# (7)!
|
|
||||||
print(miner_data.hashrate) # hashrate of the miner in TH/s
|
print(miner_data.hashrate) # hashrate of the miner in TH/s
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(gather_miner_data())# (9)!
|
asyncio.run(gather_miner_data())
|
||||||
```
|
```
|
||||||
|
---
|
||||||
1. `asyncio` for handling the async part.
|
##### Multiple miners
|
||||||
2. `get_miner` handles the miner type selection.
|
|
||||||
3. Define an async function to allow awaiting.
|
|
||||||
4. Get the miner.
|
|
||||||
5. Make sure the miner exists.
|
|
||||||
If this result is `None`, the miner may be offline.
|
|
||||||
6. Get data from the miner.
|
|
||||||
7. All the data from the dataclass.
|
|
||||||
8. Hashrate of the miner, with unit information.
|
|
||||||
9. Get the miner data asynchronously with asyncio.run().
|
|
||||||
|
|
||||||
### Multiple miners
|
|
||||||
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
|
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
|
||||||
```python
|
```python
|
||||||
import asyncio# (1)!
|
import asyncio # asyncio for handling the async part
|
||||||
from pyasic.network import MinerNetwork# (2)!
|
from pyasic.network import MinerNetwork # miner network handles the scanning
|
||||||
|
|
||||||
|
|
||||||
async def gather_miner_data():# (3)!
|
async def gather_miner_data(): # define async scan function to allow awaiting
|
||||||
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
network = MinerNetwork.from_subnet("192.168.1.50/24")
|
||||||
miners = await network.scan()# (5)!
|
miners = await network.scan()
|
||||||
|
|
||||||
|
# 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])
|
all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners])
|
||||||
|
|
||||||
for miner_data in all_miner_data:
|
for miner_data in all_miner_data:
|
||||||
print(miner_data)# (7)!
|
print(miner_data) # print out all the data one by one
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(gather_miner_data())# (8)!
|
asyncio.run(gather_miner_data())
|
||||||
```
|
```
|
||||||
|
|
||||||
1. `asyncio` for handling the async part.
|
---
|
||||||
2. `MinerNetwork` handles the scanning.
|
|
||||||
3. Define an async function to allow awaiting.
|
|
||||||
4. Create a miner network.
|
|
||||||
5. Scan for miners asynchronously.
|
|
||||||
6. Use `asyncio.gather()` with all the miners' `get_data()` functions to make them run together.
|
|
||||||
7. Print out the data one at a time.
|
|
||||||
8. Get the miner data asynchronously with asyncio.run().
|
|
||||||
|
|
||||||
## Miner control
|
## Miner control
|
||||||
---
|
---
|
||||||
`pyasic` exposes a standard interface for each miner using control functions.
|
`pyasic` exposes a standard interface for each miner using control functions.
|
||||||
Every miner class in `pyasic` must implement all the following control functions.
|
Every miner class in `pyasic` must implement all the control functions defined in [`MinerProtocol`][pyasic.miners.base.MinerProtocol].
|
||||||
|
|
||||||
|
These functions are
|
||||||
[`check_light`][pyasic.miners.base.MinerProtocol.check_light],
|
[`check_light`][pyasic.miners.base.MinerProtocol.check_light],
|
||||||
[`fault_light_off`][pyasic.miners.base.MinerProtocol.fault_light_off],
|
[`fault_light_off`][pyasic.miners.base.MinerProtocol.fault_light_off],
|
||||||
[`fault_light_on`][pyasic.miners.base.MinerProtocol.fault_light_on],
|
[`fault_light_on`][pyasic.miners.base.MinerProtocol.fault_light_on],
|
||||||
@@ -170,41 +166,35 @@ Every miner class in `pyasic` must implement all the following control functions
|
|||||||
[`send_config`][pyasic.miners.base.MinerProtocol.send_config], and
|
[`send_config`][pyasic.miners.base.MinerProtocol.send_config], and
|
||||||
[`set_power_limit`][pyasic.miners.base.MinerProtocol.set_power_limit].
|
[`set_power_limit`][pyasic.miners.base.MinerProtocol.set_power_limit].
|
||||||
|
|
||||||
### Usage
|
##### Usage
|
||||||
```python
|
```python
|
||||||
import asyncio# (1)!
|
import asyncio
|
||||||
from pyasic import get_miner# (2)!
|
from pyasic import get_miner
|
||||||
|
|
||||||
|
|
||||||
async def set_fault_light():# (3)!
|
async def set_fault_light():
|
||||||
miner = await get_miner("192.168.1.20")# (4)!
|
miner = await get_miner("192.168.1.20")
|
||||||
|
|
||||||
await miner.fault_light_on()# (5)!
|
# call control function
|
||||||
|
await miner.fault_light_on()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(set_fault_light())# (6)!
|
asyncio.run(set_fault_light())
|
||||||
```
|
```
|
||||||
|
|
||||||
1. `asyncio` for handling the async part.
|
---
|
||||||
2. `get_miner` handles the miner type selection.
|
|
||||||
3. Define an async function to allow awaiting.
|
|
||||||
4. Get the miner.
|
|
||||||
5. Call the miner control function.
|
|
||||||
6. Call the control function asynchronously with asyncio.run().
|
|
||||||
|
|
||||||
|
|
||||||
## Helper dataclasses
|
## Helper dataclasses
|
||||||
---
|
---
|
||||||
|
|
||||||
### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
##### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
||||||
|
|
||||||
`pyasic` implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
`pyasic` implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [`MinerData`][pyasic.data.MinerData]
|
##### [`MinerData`][pyasic.data.MinerData]
|
||||||
|
|
||||||
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`][pyasic.miners.base.MinerProtocol.get_data] function, and is used to have a consistent dataset across all returns.
|
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns.
|
||||||
|
|
||||||
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
||||||
|
|
||||||
@@ -223,13 +213,13 @@ average_data = sum(list_of_miner_data, start=MinerData("0.0.0.0"))/len(list_of_m
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [`MinerConfig`][pyasic.config.MinerConfig]
|
##### [`MinerConfig`][pyasic.config.MinerConfig]
|
||||||
|
|
||||||
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
||||||
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`][pyasic.miners.base.MinerProtocol.get_config].
|
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`](#get-config).
|
||||||
|
|
||||||
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
||||||
In most cases these helper functions should not be used, as [`send_config()`][pyasic.miners.base.MinerProtocol.send_config] takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
||||||
|
|
||||||
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
||||||
```python
|
```python
|
||||||
@@ -251,6 +241,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
## Settings
|
## Settings
|
||||||
---
|
---
|
||||||
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows:
|
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows:
|
||||||
@@ -258,30 +249,25 @@ if __name__ == "__main__":
|
|||||||
```python
|
```python
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
|
|
||||||
settings.update("default_antminer_web_password", "my_pwd")
|
settings.update("default_antminer_password", "my_pwd")
|
||||||
```
|
```
|
||||||
|
|
||||||
### Default values:
|
##### Default values:
|
||||||
```
|
```
|
||||||
"network_ping_retries": 1,
|
"network_ping_retries": 1,
|
||||||
"network_ping_timeout": 3,
|
"network_ping_timeout": 3,
|
||||||
"network_scan_semaphore": None,
|
"network_scan_threads": 300,
|
||||||
"factory_get_retries": 1,
|
"factory_get_retries": 1,
|
||||||
"factory_get_timeout": 3,
|
"factory_get_timeout": 3,
|
||||||
"get_data_retries": 1,
|
"get_data_retries": 1,
|
||||||
"api_function_timeout": 5,
|
"api_function_timeout": 5,
|
||||||
"antminer_mining_mode_as_str": False,
|
"antminer_mining_mode_as_str": False,
|
||||||
"default_whatsminer_rpc_password": "admin",
|
"default_whatsminer_password": "admin",
|
||||||
"default_innosilicon_web_password": "admin",
|
"default_innosilicon_password": "admin",
|
||||||
"default_antminer_web_password": "root",
|
"default_antminer_password": "root",
|
||||||
"default_bosminer_web_password": "root",
|
"default_bosminer_password": "root",
|
||||||
"default_vnish_web_password": "admin",
|
"default_vnish_password": "admin",
|
||||||
"default_goldshell_web_password": "123456789",
|
"default_goldshell_password": "123456789",
|
||||||
"default_auradine_web_password": "admin",
|
|
||||||
"default_epic_web_password": "letmein",
|
|
||||||
"default_hive_web_password": "admin",
|
|
||||||
"default_antminer_ssh_password": "miner",
|
|
||||||
"default_bosminer_ssh_password": "root",
|
|
||||||
|
|
||||||
# ADVANCED
|
# ADVANCED
|
||||||
# Only use this if you know what you are doing
|
# Only use this if you know what you are doing
|
||||||
|
|||||||
@@ -1,29 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X15 Models
|
## X15 Models
|
||||||
|
|
||||||
## Z15 (Stock)
|
## Z15
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15
|
::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Z15 Pro (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X15.Z15.BMMinerZ15Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,211 +1,115 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X17 Models
|
## X17 Models
|
||||||
|
|
||||||
## S17 (Stock)
|
## S17
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S17 Pro (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S17+ (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## S17+
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S17e (Stock)
|
## S17 Pro
|
||||||
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro
|
||||||
- [ ] Shutdowns
|
handler: python
|
||||||
- [ ] Power Modes
|
options:
|
||||||
- [ ] Setpoints
|
show_root_heading: false
|
||||||
- [ ] Presets
|
heading_level: 4
|
||||||
|
|
||||||
|
## S17e
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T17 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## T17
|
||||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
|
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T17+ (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## T17+
|
||||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus
|
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T17e (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## T17e
|
||||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e
|
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S17 (BOS+)
|
## S17 (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S17 Pro (BOS+)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S17+ (BOS+)
|
## S17+ (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
## S17 Pro (BOS+)
|
||||||
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## S17e (BOS+)
|
## S17e (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17e
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T17 (BOS+)
|
## T17 (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17
|
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T17+ (BOS+)
|
## T17+ (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17Plus
|
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T17e (BOS+)
|
## T17e (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17e
|
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S17 Pro (VNish)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S17+ (VNish)
|
## S17+ (VNish)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
|
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
## S17 Pro (VNish)
|
||||||
|
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,211 +1,59 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X21 Models
|
## X21 Models
|
||||||
|
|
||||||
## S21 (Stock)
|
## S21
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21
|
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S21 Hydro (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Hydro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S21 Pro (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S21+ (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Plus
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## T21 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## T21
|
||||||
::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21
|
::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S21 (BOS+)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## S21
|
||||||
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21
|
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S21 Pro (BOS+)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## T21 (BOS+)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X21.T21.BOSMinerT21
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S21 (VNish)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## T21 (VNish)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X21.T21.VNishT21
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S21 (ePIC)
|
## S21 (ePIC)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.epic.X21.S21.ePICS21
|
::: pyasic.miners.antminer.epic.X21.S21.ePICS21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S21 Pro (ePIC)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.epic.X21.S21.ePICS21Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## T21 (ePIC)
|
## T21 (ePIC)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.epic.X21.T21.ePICT21
|
::: pyasic.miners.antminer.epic.X21.T21.ePICT21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S21 (LuxOS)
|
## S21 (LuxOS)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.luxos.X21.S21.LUXMinerS21
|
::: pyasic.miners.antminer.luxos.X21.S21.LUXMinerS21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S21 (MaraFW)
|
## S21 (MaraFW)
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.marathon.X21.S21.MaraS21
|
::: pyasic.miners.antminer.marathon.X21.S21.MaraS21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T21 (MaraFW)
|
## T21 (MaraFW)
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.marathon.X21.T21.MaraT21
|
::: pyasic.miners.antminer.marathon.X21.T21.MaraT21
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +1,38 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X3 Models
|
## X3 Models
|
||||||
|
|
||||||
## D3 (Stock)
|
## D3
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3
|
::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## HS3 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## HS3
|
||||||
::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3
|
::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## KA3 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X3.KA3.BMMinerKA3
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS3 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X3.KS3.BMMinerKS3
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## L3+ (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## L3+
|
||||||
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
|
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## L3+ (VNish)
|
## L3+ (VNish)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
|
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
## L3+ (VNish)
|
||||||
|
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X5 Models
|
## X5 Models
|
||||||
|
|
||||||
## DR5 (Stock)
|
## DR5
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5
|
::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## KS5 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS5 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,17 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X7 Models
|
## X7 Models
|
||||||
|
|
||||||
## D7 (Stock)
|
## L7
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## K7 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X7.K7.BMMinerK7
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## L7 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
|
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## L7 (VNish)
|
## L7 (VNish)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
|
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,133 +1,59 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X9 Models
|
## X9 Models
|
||||||
|
|
||||||
## D9 (Stock)
|
## E9Pro
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## E9Pro (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro
|
::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## L9 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## S9 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## S9
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S9i (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## S9i
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S9j (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## S9j
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T9 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## T9
|
||||||
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
|
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S9 (BOS+)
|
## S9 (BOS+)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
|
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## T9 (Hive)
|
## T9 (Hive)
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
|
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## S9 (LuxOS)
|
## S9 (LuxOS)
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [x] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9
|
::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,24 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## AD Models
|
## AD Models
|
||||||
|
|
||||||
## AT1500 (Stock)
|
## AT1500
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500
|
::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## AT2860 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## AT2860
|
||||||
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860
|
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## AT2880 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## AT2880
|
||||||
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880
|
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,17 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## AI Models
|
## AI Models
|
||||||
|
|
||||||
## AI2500 (Stock)
|
## AI2500
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500
|
::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## AI3680 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## AI3680
|
||||||
::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680
|
::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,17 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## AT Models
|
## AT Models
|
||||||
|
|
||||||
## AD2500 (Stock)
|
## AD2500
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500
|
::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## AD3500 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## AD3500
|
||||||
::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500
|
::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,24 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A10X Models
|
## A10X Models
|
||||||
|
|
||||||
## Avalon 1026 (Stock)
|
## Avalon 1026
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
|
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 1047 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## Avalon 1047
|
||||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
|
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 1066 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## Avalon 1066
|
||||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
|
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A11X Models
|
## A11X Models
|
||||||
|
|
||||||
## Avalon 1126 Pro (Stock)
|
## Avalon 1166 Pro
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A11X.A1126.CGMinerAvalon1126Pro
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## Avalon 1166 Pro (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
|
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A12X Models
|
## A12X Models
|
||||||
|
|
||||||
## Avalon 1246 (Stock)
|
## Avalon 1246
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246
|
::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## A15X Models
|
|
||||||
|
|
||||||
## Avalon 1566 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A15X.A1566.CGMinerAvalon1566
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,42 +1,24 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A7X Models
|
## A7X Models
|
||||||
|
|
||||||
## Avalon 721 (Stock)
|
## Avalon 721
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
|
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 741 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## Avalon 741
|
||||||
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
|
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 761 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## Avalon 761
|
||||||
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
|
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,24 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A8X Models
|
## A8X Models
|
||||||
|
|
||||||
## Avalon 821 (Stock)
|
## Avalon 821
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
|
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 841 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## Avalon 841
|
||||||
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
|
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 851 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## Avalon 851
|
||||||
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
|
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A9X Models
|
## A9X Models
|
||||||
|
|
||||||
## Avalon 921 (Stock)
|
## Avalon 921
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
|
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## nano Models
|
|
||||||
|
|
||||||
## Avalon Nano 3 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## Avalon Nano 3s (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3s
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## Modern Hiveon Backend
|
## Hiveon Backend
|
||||||
|
|
||||||
::: pyasic.miners.backends.hiveon.HiveonModern
|
::: pyasic.miners.backends.hiveon.Hiveon
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
## Old Hiveon Backend
|
|
||||||
::: pyasic.miners.backends.hiveon.HiveonOld
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
|
|||||||
@@ -10,3 +10,8 @@ You may not instantiate this class on its own, only subclass from it.
|
|||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
::: pyasic.miners.base.MinerProtocol
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## BM Models
|
|
||||||
|
|
||||||
## Ultra (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.bitaxe.espminer.BM.BM1366.BitAxeUltra
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## Supra (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## Gamma (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.bitaxe.espminer.BM.BM1370.BitAxeGamma
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## Max (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## blockminer Models
|
|
||||||
|
|
||||||
## BlockMiner 520i (ePIC)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner520i
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## BlockMiner 720i (ePIC)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner720i
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## BlockMiner eLITE 1.0 (ePIC)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMinerELITE1
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## BMM Models
|
|
||||||
|
|
||||||
## BMM100 (BOS+)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.braiins.braiins.BMM.BMM.BraiinsBMM100
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## BMM101 (BOS+)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.braiins.braiins.BMM.BMM.BraiinsBMM101
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## DGX Models
|
|
||||||
|
|
||||||
## DG1+ (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1Plus
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,10 +1,91 @@
|
|||||||
## Control functionality
|
## Control functionality
|
||||||
|
|
||||||
All control functionality is outlined by the [`MinerProtocol`][pyasic.miners.base.MinerProtocol] class.
|
### Check Light
|
||||||
|
::: pyasic.miners.base.MinerProtocol.check_light
|
||||||
## Miner Protocol
|
handler: python
|
||||||
|
options:
|
||||||
::: pyasic.miners.base.MinerProtocol
|
heading_level: 4
|
||||||
|
|
||||||
|
### Fault Light Off
|
||||||
|
::: pyasic.miners.base.MinerProtocol.fault_light_off
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Fault Light On
|
||||||
|
::: pyasic.miners.base.MinerProtocol.fault_light_on
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Get Config
|
||||||
|
::: pyasic.miners.base.MinerProtocol.get_config
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Get Data
|
||||||
|
::: pyasic.miners.base.MinerProtocol.get_data
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Get Errors
|
||||||
|
::: pyasic.miners.base.MinerProtocol.get_errors
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Get Hostname
|
||||||
|
::: pyasic.miners.base.MinerProtocol.get_hostname
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Get Model
|
||||||
|
::: pyasic.miners.base.MinerProtocol.get_model
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Reboot
|
||||||
|
::: pyasic.miners.base.MinerProtocol.reboot
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Restart Backend
|
||||||
|
::: pyasic.miners.base.MinerProtocol.restart_backend
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Stop Mining
|
||||||
|
::: pyasic.miners.base.MinerProtocol.stop_mining
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Resume Mining
|
||||||
|
::: pyasic.miners.base.MinerProtocol.resume_mining
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Is Mining
|
||||||
|
::: pyasic.miners.base.MinerProtocol.is_mining
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Send Config
|
||||||
|
::: pyasic.miners.base.MinerProtocol.send_config
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
### Set Power Limit
|
||||||
|
::: pyasic.miners.base.MinerProtocol.set_power_limit
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|||||||
@@ -1,42 +1,24 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X5 Models
|
## X5 Models
|
||||||
|
|
||||||
## CK5 (Stock)
|
## CK5
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5
|
::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## HS5 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## HS5
|
||||||
::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5
|
::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## KD5 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## KD5
|
||||||
::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5
|
::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,17 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## XBox Models
|
## XBox Models
|
||||||
|
|
||||||
## KD Box II (Stock)
|
## KD Box II
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII
|
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## KD Box Pro (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
|
## KD Box Pro
|
||||||
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro
|
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## XMax Models
|
## XMax Models
|
||||||
|
|
||||||
## KD Max (Stock)
|
## KD Max
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax
|
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## DX Models
|
|
||||||
|
|
||||||
## D10 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.hammer.blackminer.DX.D10.HammerD10
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## KSX Models
|
|
||||||
|
|
||||||
## KS0 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS0.IceRiverKS0
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS1 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS1.IceRiverKS1
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS2 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS3 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS3L (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3L
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS3M (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3M
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS5 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS5L (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5L
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## KS5M (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5M
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A10X Models
|
## A10X Models
|
||||||
|
|
||||||
## A10X (Stock)
|
## A10X
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
|
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## A11X Models
|
|
||||||
|
|
||||||
## A11 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.innosilicon.cgminer.A11X.A11.InnosiliconA11
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## A11MX (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.innosilicon.cgminer.A11X.A11M.InnosiliconA11MX
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## T3X Models
|
## T3X Models
|
||||||
|
|
||||||
## T3H+ (Stock)
|
## T3H+
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
|
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## LV Models
|
|
||||||
|
|
||||||
## LV07 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.luckyminer.espminer.LV.LV07.LuckyMinerLV07
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## LV08 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.luckyminer.espminer.LV.LV08.LuckyMinerLV08
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## DX Models
|
|
||||||
|
|
||||||
## D1 (Stock)
|
|
||||||
|
|
||||||
- [ ] Shutdowns
|
|
||||||
- [ ] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.volcminer.blackminer.DX.D1.VolcMinerD1
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
@@ -1,172 +1,94 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## M2X Models
|
## M2X Models
|
||||||
|
|
||||||
## M20P V10 (Stock)
|
## M20 V10
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## M20P V30 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## M20S+ V30 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## M20S V10 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## M20S V20 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## M20S V30 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
## M20 V10 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
|
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## M21S+ V20 (Stock)
|
## M20S V10
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## M21S V20 (Stock)
|
## M20S V20
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## M21S V60 (Stock)
|
## M20S V30
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## M21S V70 (Stock)
|
## M20P V10
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [ ] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## M21 V10 (Stock)
|
## M20P V30
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
- [x] Shutdowns
|
## M20S+ V30
|
||||||
- [x] Power Modes
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
|
||||||
- [ ] Setpoints
|
handler: python
|
||||||
- [ ] Presets
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M21 V10
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
|
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
## M29 V10 (Stock)
|
## M21S V20
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
- [x] Shutdowns
|
## M21S V60
|
||||||
- [x] Power Modes
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
|
||||||
- [ ] Setpoints
|
handler: python
|
||||||
- [ ] Presets
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M21S V70
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M21S+ V20
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M29 V10
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
|
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
# pyasic
|
|
||||||
## M7X Models
|
|
||||||
|
|
||||||
## M70 VM30 (Stock)
|
|
||||||
|
|
||||||
- [x] Shutdowns
|
|
||||||
- [x] Power Modes
|
|
||||||
- [x] Setpoints
|
|
||||||
- [ ] Presets
|
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M7X.M70.BTMinerM70VM30
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 0
|
|
||||||
|
|
||||||
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
jinja2<3.1.3
|
||||||
|
mkdocs
|
||||||
|
mkdocstrings[python]
|
||||||
@@ -13,17 +13,13 @@ Settings options:
|
|||||||
- `get_data_retries`
|
- `get_data_retries`
|
||||||
- `api_function_timeout`
|
- `api_function_timeout`
|
||||||
- `antminer_mining_mode_as_str`
|
- `antminer_mining_mode_as_str`
|
||||||
- `default_whatsminer_rpc_password`
|
- `default_whatsminer_password`
|
||||||
- `default_innosilicon_web_password`
|
- `default_innosilicon_password`
|
||||||
- `default_antminer_web_password`
|
- `default_antminer_password`
|
||||||
- `default_bosminer_web_password`
|
- `default_bosminer_password`
|
||||||
- `default_vnish_web_password`
|
- `default_vnish_password`
|
||||||
- `default_goldshell_web_password`
|
- `default_goldshell_password`
|
||||||
- `default_auradine_web_password`
|
- `socket_linger_time`
|
||||||
- `default_epic_web_password`
|
|
||||||
- `default_hive_web_password`
|
|
||||||
- `default_antminer_ssh_password`
|
|
||||||
- `default_bosminer_ssh_password`
|
|
||||||
|
|
||||||
|
|
||||||
### get
|
### get
|
||||||
|
|||||||
40
mkdocs.yml
40
mkdocs.yml
@@ -1,36 +1,5 @@
|
|||||||
site_name: pyasic
|
site_name: pyasic
|
||||||
repo_url: https://github.com/UpstreamData/pyasic
|
repo_url: https://github.com/UpstreamData/pyasic
|
||||||
site_url: !ENV SITE_URL
|
|
||||||
theme:
|
|
||||||
name: material
|
|
||||||
features:
|
|
||||||
- content.code.copy
|
|
||||||
- content.code.annotate
|
|
||||||
palette:
|
|
||||||
- media: "(prefers-color-scheme)"
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-auto
|
|
||||||
name: Switch to light mode
|
|
||||||
- media: "(prefers-color-scheme: light)"
|
|
||||||
scheme: default
|
|
||||||
toggle:
|
|
||||||
icon: material/weather-night
|
|
||||||
name: Switch to dark mode
|
|
||||||
- media: "(prefers-color-scheme: dark)"
|
|
||||||
scheme: slate
|
|
||||||
toggle:
|
|
||||||
icon: material/weather-sunny
|
|
||||||
name: Switch to auto mode
|
|
||||||
markdown_extensions:
|
|
||||||
- pymdownx.highlight:
|
|
||||||
anchor_linenums: true
|
|
||||||
line_spans: __span
|
|
||||||
pygments_lang_class: true
|
|
||||||
- pymdownx.tasklist:
|
|
||||||
custom_checkbox: true
|
|
||||||
- pymdownx.inlinehilite
|
|
||||||
- pymdownx.snippets
|
|
||||||
- pymdownx.superfences
|
|
||||||
nav:
|
nav:
|
||||||
- Introduction: "index.md"
|
- Introduction: "index.md"
|
||||||
- Miners:
|
- Miners:
|
||||||
@@ -81,8 +50,6 @@ nav:
|
|||||||
- Antminer X17: "miners/antminer/X17.md"
|
- Antminer X17: "miners/antminer/X17.md"
|
||||||
- Antminer X19: "miners/antminer/X19.md"
|
- Antminer X19: "miners/antminer/X19.md"
|
||||||
- Antminer X21: "miners/antminer/X21.md"
|
- Antminer X21: "miners/antminer/X21.md"
|
||||||
- Braiins Mini Miners: "miners/braiins/BMM.md"
|
|
||||||
- Avalon Nano: "miners/avalonminer/nano.md"
|
|
||||||
- Avalon 7X: "miners/avalonminer/A7X.md"
|
- Avalon 7X: "miners/avalonminer/A7X.md"
|
||||||
- Avalon 8X: "miners/avalonminer/A8X.md"
|
- Avalon 8X: "miners/avalonminer/A8X.md"
|
||||||
- Avalon 9X: "miners/avalonminer/A9X.md"
|
- Avalon 9X: "miners/avalonminer/A9X.md"
|
||||||
@@ -93,21 +60,14 @@ nav:
|
|||||||
- Whatsminer M3X: "miners/whatsminer/M3X.md"
|
- Whatsminer M3X: "miners/whatsminer/M3X.md"
|
||||||
- Whatsminer M5X: "miners/whatsminer/M5X.md"
|
- Whatsminer M5X: "miners/whatsminer/M5X.md"
|
||||||
- Whatsminer M6X: "miners/whatsminer/M6X.md"
|
- Whatsminer M6X: "miners/whatsminer/M6X.md"
|
||||||
- Whatsminer M7X: "miners/whatsminer/M7X.md"
|
|
||||||
- Innosilicon T3X: "miners/innosilicon/T3X.md"
|
- Innosilicon T3X: "miners/innosilicon/T3X.md"
|
||||||
- Innosilicon A10X: "miners/innosilicon/A10X.md"
|
- Innosilicon A10X: "miners/innosilicon/A10X.md"
|
||||||
- Innosilicon A11X: "miners/innosilicon/A11X.md"
|
|
||||||
- Goldshell X5: "miners/goldshell/X5.md"
|
- Goldshell X5: "miners/goldshell/X5.md"
|
||||||
- Goldshell XMax: "miners/goldshell/XMax.md"
|
- Goldshell XMax: "miners/goldshell/XMax.md"
|
||||||
- Goldshell XBox: "miners/goldshell/XBox.md"
|
- Goldshell XBox: "miners/goldshell/XBox.md"
|
||||||
- Auradine AD: "miners/auradine/AD.md"
|
- Auradine AD: "miners/auradine/AD.md"
|
||||||
- Auradine AI: "miners/auradine/AI.md"
|
- Auradine AI: "miners/auradine/AI.md"
|
||||||
- Auradine AT: "miners/auradine/AT.md"
|
- Auradine AT: "miners/auradine/AT.md"
|
||||||
- Blockminer: "miners/blockminer/blockminer.md"
|
|
||||||
- BitAxe BM: "miners/bitaxe/BM.md"
|
|
||||||
- Hammer DX: "miners/hammer/DX.md"
|
|
||||||
- Iceriver KSX: "miners/iceriver/KSX.md"
|
|
||||||
- Volcminer DX: "miners/volcminer/DX.md"
|
|
||||||
- Base Miner: "miners/base_miner.md"
|
- Base Miner: "miners/base_miner.md"
|
||||||
- Settings:
|
- Settings:
|
||||||
- Settings: "settings/settings.md"
|
- Settings: "settings/settings.md"
|
||||||
|
|||||||
1678
poetry.lock
generated
1678
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import importlib.metadata
|
|
||||||
|
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import MinerData
|
from pyasic.data import MinerData
|
||||||
@@ -24,5 +22,3 @@ from pyasic.network import MinerNetwork
|
|||||||
from pyasic.rpc import *
|
from pyasic.rpc import *
|
||||||
from pyasic.ssh import *
|
from pyasic.ssh import *
|
||||||
from pyasic.web import *
|
from pyasic.web import *
|
||||||
|
|
||||||
__version__ = importlib.metadata.version("pyasic")
|
|
||||||
|
|||||||
@@ -13,40 +13,33 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
from dataclasses import asdict, dataclass, field
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pyasic.config.fans import FanModeConfig
|
||||||
|
from pyasic.config.mining import MiningModeConfig
|
||||||
from pyasic.config.fans import FanMode, FanModeConfig, FanModeNormal
|
|
||||||
from pyasic.config.mining import MiningMode, MiningModeConfig
|
|
||||||
from pyasic.config.mining.scaling import ScalingConfig
|
|
||||||
from pyasic.config.pools import PoolConfig
|
from pyasic.config.pools import PoolConfig
|
||||||
|
from pyasic.config.power_scaling import PowerScalingConfig
|
||||||
from pyasic.config.temperature import TemperatureConfig
|
from pyasic.config.temperature import TemperatureConfig
|
||||||
from pyasic.misc import merge_dicts
|
from pyasic.misc import merge_dicts
|
||||||
|
|
||||||
|
|
||||||
class MinerConfig(BaseModel):
|
@dataclass
|
||||||
|
class MinerConfig:
|
||||||
"""Represents the configuration for a miner including pool configuration,
|
"""Represents the configuration for a miner including pool configuration,
|
||||||
fan mode, temperature settings, mining mode, and power scaling."""
|
fan mode, temperature settings, mining mode, and power scaling."""
|
||||||
|
pools: PoolConfig = field(default_factory=PoolConfig.default)
|
||||||
class Config:
|
fan_mode: FanModeConfig = field(default_factory=FanModeConfig.default)
|
||||||
arbitrary_types_allowed = True
|
temperature: TemperatureConfig = field(default_factory=TemperatureConfig.default)
|
||||||
|
mining_mode: MiningModeConfig = field(default_factory=MiningModeConfig.default)
|
||||||
pools: PoolConfig = Field(default_factory=PoolConfig.default)
|
power_scaling: PowerScalingConfig = field(
|
||||||
fan_mode: FanMode = Field(default_factory=FanModeConfig.default)
|
default_factory=PowerScalingConfig.default
|
||||||
temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default)
|
)
|
||||||
mining_mode: MiningMode = Field(default_factory=MiningModeConfig.default)
|
|
||||||
|
|
||||||
def __getitem__(self, item):
|
|
||||||
try:
|
|
||||||
return getattr(self, item)
|
|
||||||
except AttributeError:
|
|
||||||
raise KeyError
|
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
"""Converts the MinerConfig object to a dictionary."""
|
"""Converts the MinerConfig object to a dictionary."""
|
||||||
return self.model_dump()
|
return asdict(self)
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str | None = None) -> dict:
|
def as_am_modern(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for modern Antminers."""
|
"""Generates the configuration in the format suitable for modern Antminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_am_modern(),
|
**self.fan_mode.as_am_modern(),
|
||||||
@@ -54,147 +47,97 @@ class MinerConfig(BaseModel):
|
|||||||
**self.mining_mode.as_am_modern(),
|
**self.mining_mode.as_am_modern(),
|
||||||
**self.pools.as_am_modern(user_suffix=user_suffix),
|
**self.pools.as_am_modern(user_suffix=user_suffix),
|
||||||
**self.temperature.as_am_modern(),
|
**self.temperature.as_am_modern(),
|
||||||
|
**self.power_scaling.as_am_modern(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
|
def as_wm(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for modern Hiveon."""
|
|
||||||
return {
|
|
||||||
**self.fan_mode.as_hiveon_modern(),
|
|
||||||
"freq-level": "100",
|
|
||||||
**self.mining_mode.as_hiveon_modern(),
|
|
||||||
**self.pools.as_hiveon_modern(user_suffix=user_suffix),
|
|
||||||
**self.temperature.as_hiveon_modern(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
|
||||||
"""Generates the configuration in the format suitable for modern Elphapex."""
|
|
||||||
return {
|
|
||||||
**self.fan_mode.as_elphapex(),
|
|
||||||
"fc-freq-level": "100",
|
|
||||||
**self.mining_mode.as_elphapex(),
|
|
||||||
**self.pools.as_elphapex(user_suffix=user_suffix),
|
|
||||||
**self.temperature.as_elphapex(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str | None = None) -> dict:
|
|
||||||
"""Generates the configuration in the format suitable for Whatsminers."""
|
"""Generates the configuration in the format suitable for Whatsminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_wm(),
|
**self.fan_mode.as_wm(),
|
||||||
**self.mining_mode.as_wm(),
|
**self.mining_mode.as_wm(),
|
||||||
**self.pools.as_wm(user_suffix=user_suffix),
|
**self.pools.as_wm(user_suffix=user_suffix),
|
||||||
**self.temperature.as_wm(),
|
**self.temperature.as_wm(),
|
||||||
|
**self.power_scaling.as_wm(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str | None = None) -> dict:
|
def as_am_old(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for old versions of Antminers."""
|
"""Generates the configuration in the format suitable for old versions of Antminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_am_old(),
|
**self.fan_mode.as_am_old(),
|
||||||
**self.mining_mode.as_am_old(),
|
**self.mining_mode.as_am_old(),
|
||||||
**self.pools.as_am_old(user_suffix=user_suffix),
|
**self.pools.as_am_old(user_suffix=user_suffix),
|
||||||
**self.temperature.as_am_old(),
|
**self.temperature.as_am_old(),
|
||||||
|
**self.power_scaling.as_am_old(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str | None = None) -> dict:
|
def as_goldshell(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for Goldshell miners."""
|
"""Generates the configuration in the format suitable for Goldshell miners."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_goldshell(),
|
**self.fan_mode.as_goldshell(),
|
||||||
**self.mining_mode.as_goldshell(),
|
**self.mining_mode.as_goldshell(),
|
||||||
**self.pools.as_goldshell(user_suffix=user_suffix),
|
**self.pools.as_goldshell(user_suffix=user_suffix),
|
||||||
**self.temperature.as_goldshell(),
|
**self.temperature.as_goldshell(),
|
||||||
|
**self.power_scaling.as_goldshell(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str | None = None) -> dict:
|
def as_avalon(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for Avalonminers."""
|
"""Generates the configuration in the format suitable for Avalonminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_avalon(),
|
**self.fan_mode.as_avalon(),
|
||||||
**self.mining_mode.as_avalon(),
|
**self.mining_mode.as_avalon(),
|
||||||
**self.pools.as_avalon(user_suffix=user_suffix),
|
**self.pools.as_avalon(user_suffix=user_suffix),
|
||||||
**self.temperature.as_avalon(),
|
**self.temperature.as_avalon(),
|
||||||
|
**self.power_scaling.as_avalon(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str | None = None) -> dict:
|
def as_inno(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for Innosilicon miners."""
|
"""Generates the configuration in the format suitable for Innosilicon miners."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_inno(),
|
**self.fan_mode.as_inno(),
|
||||||
**self.mining_mode.as_inno(),
|
**self.mining_mode.as_inno(),
|
||||||
**self.pools.as_inno(user_suffix=user_suffix),
|
**self.pools.as_inno(user_suffix=user_suffix),
|
||||||
**self.temperature.as_inno(),
|
**self.temperature.as_inno(),
|
||||||
|
**self.power_scaling.as_inno(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
def as_bosminer(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the bosminer.toml format."""
|
"""Generates the configuration in the bosminer.toml format."""
|
||||||
return {
|
return {
|
||||||
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
|
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
|
||||||
**self.mining_mode.as_bosminer(),
|
**self.mining_mode.as_bosminer(),
|
||||||
**self.pools.as_bosminer(user_suffix=user_suffix),
|
**self.pools.as_bosminer(user_suffix=user_suffix),
|
||||||
|
**self.power_scaling.as_bosminer(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str | None = None) -> dict:
|
def as_boser(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for BOSer."""
|
""""Generates the configuration in the format suitable for BOSer."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_boser(),
|
**self.fan_mode.as_boser(),
|
||||||
**self.temperature.as_boser(),
|
**self.temperature.as_boser(),
|
||||||
**self.mining_mode.as_boser(),
|
**self.mining_mode.as_boser(),
|
||||||
**self.pools.as_boser(user_suffix=user_suffix),
|
**self.pools.as_boser(user_suffix=user_suffix),
|
||||||
|
**self.power_scaling.as_boser(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_epic(self, user_suffix: str | None = None) -> dict:
|
def as_epic(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for ePIC miners."""
|
"""Generates the configuration in the format suitable for ePIC miners."""
|
||||||
return {
|
return {
|
||||||
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
|
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
|
||||||
**self.mining_mode.as_epic(),
|
**self.mining_mode.as_epic(),
|
||||||
**self.pools.as_epic(user_suffix=user_suffix),
|
**self.pools.as_epic(user_suffix=user_suffix),
|
||||||
|
**self.power_scaling.as_epic(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
def as_auradine(self, user_suffix: str = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for Auradine miners."""
|
"""Generates the configuration in the format suitable for Auradine miners."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_auradine(),
|
**self.fan_mode.as_auradine(),
|
||||||
**self.temperature.as_auradine(),
|
**self.temperature.as_auradine(),
|
||||||
**self.mining_mode.as_auradine(),
|
**self.mining_mode.as_auradine(),
|
||||||
**self.pools.as_auradine(user_suffix=user_suffix),
|
**self.pools.as_auradine(user_suffix=user_suffix),
|
||||||
|
**self.power_scaling.as_auradine(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_mara(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
**self.fan_mode.as_mara(),
|
|
||||||
**self.temperature.as_mara(),
|
|
||||||
**self.mining_mode.as_mara(),
|
|
||||||
**self.pools.as_mara(user_suffix=user_suffix),
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
**self.fan_mode.as_espminer(),
|
|
||||||
**self.temperature.as_espminer(),
|
|
||||||
**self.mining_mode.as_espminer(),
|
|
||||||
**self.pools.as_espminer(user_suffix=user_suffix),
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_luxos(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
**self.fan_mode.as_luxos(),
|
|
||||||
**self.temperature.as_luxos(),
|
|
||||||
**self.mining_mode.as_luxos(),
|
|
||||||
**self.pools.as_luxos(user_suffix=user_suffix),
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
|
||||||
main_cfg = {
|
|
||||||
"miner": {
|
|
||||||
**self.fan_mode.as_vnish(),
|
|
||||||
**self.temperature.as_vnish(),
|
|
||||||
**self.mining_mode.as_vnish(),
|
|
||||||
**self.pools.as_vnish(user_suffix=user_suffix),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isinstance(self.fan_mode, FanModeNormal):
|
|
||||||
main_cfg["miner"]["cooling"]["mode"]["param"] = self.temperature.target
|
|
||||||
return main_cfg
|
|
||||||
|
|
||||||
def as_hammer(self, *args, **kwargs) -> dict:
|
|
||||||
return self.as_am_modern(*args, **kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
|
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
|
||||||
"""Constructs a MinerConfig object from a dictionary."""
|
"""Constructs a MinerConfig object from a dictionary."""
|
||||||
@@ -203,6 +146,7 @@ class MinerConfig(BaseModel):
|
|||||||
mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
|
mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
|
||||||
fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
|
fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
|
||||||
temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
|
temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
|
||||||
|
power_scaling=PowerScalingConfig.from_dict(dict_conf.get("power_scaling")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -219,24 +163,6 @@ class MinerConfig(BaseModel):
|
|||||||
fan_mode=FanModeConfig.from_am_modern(web_conf),
|
fan_mode=FanModeConfig.from_am_modern(web_conf),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
|
|
||||||
"""Constructs a MinerConfig object from web configuration for Hiveon."""
|
|
||||||
return cls(
|
|
||||||
pools=PoolConfig.from_hiveon_modern(web_conf),
|
|
||||||
mining_mode=MiningModeConfig.from_hiveon_modern(web_conf),
|
|
||||||
fan_mode=FanModeConfig.from_hiveon_modern(web_conf),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_elphapex(cls, web_conf: dict) -> "MinerConfig":
|
|
||||||
"""Constructs a MinerConfig object from web configuration for modern Antminers."""
|
|
||||||
return cls(
|
|
||||||
pools=PoolConfig.from_elphapex(web_conf),
|
|
||||||
mining_mode=MiningModeConfig.from_elphapex(web_conf),
|
|
||||||
fan_mode=FanModeConfig.from_elphapex(web_conf),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
|
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
|
||||||
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
|
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
|
||||||
@@ -260,6 +186,7 @@ class MinerConfig(BaseModel):
|
|||||||
mining_mode=MiningModeConfig.from_bosminer(toml_conf),
|
mining_mode=MiningModeConfig.from_bosminer(toml_conf),
|
||||||
fan_mode=FanModeConfig.from_bosminer(toml_conf),
|
fan_mode=FanModeConfig.from_bosminer(toml_conf),
|
||||||
temperature=TemperatureConfig.from_bosminer(toml_conf),
|
temperature=TemperatureConfig.from_bosminer(toml_conf),
|
||||||
|
power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -270,6 +197,7 @@ class MinerConfig(BaseModel):
|
|||||||
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
|
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
|
||||||
fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
|
fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
|
||||||
temperature=TemperatureConfig.from_boser(grpc_miner_conf),
|
temperature=TemperatureConfig.from_boser(grpc_miner_conf),
|
||||||
|
power_scaling=PowerScalingConfig.from_boser(grpc_miner_conf),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -283,13 +211,13 @@ class MinerConfig(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict, web_presets: list[dict]) -> "MinerConfig":
|
def from_vnish(cls, web_settings: dict) -> "MinerConfig":
|
||||||
"""Constructs a MinerConfig object from web settings for VNish miners."""
|
"""Constructs a MinerConfig object from web settings for VNish miners."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_vnish(web_settings),
|
pools=PoolConfig.from_vnish(web_settings),
|
||||||
fan_mode=FanModeConfig.from_vnish(web_settings),
|
fan_mode=FanModeConfig.from_vnish(web_settings),
|
||||||
temperature=TemperatureConfig.from_vnish(web_settings),
|
temperature=TemperatureConfig.from_vnish(web_settings),
|
||||||
mining_mode=MiningModeConfig.from_vnish(web_settings, web_presets),
|
mining_mode=MiningModeConfig.from_vnish(web_settings),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -300,49 +228,3 @@ class MinerConfig(BaseModel):
|
|||||||
fan_mode=FanModeConfig.from_auradine(web_conf["fan"]),
|
fan_mode=FanModeConfig.from_auradine(web_conf["fan"]),
|
||||||
mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]),
|
mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mara(cls, web_miner_config: dict) -> "MinerConfig":
|
|
||||||
return cls(
|
|
||||||
pools=PoolConfig.from_mara(web_miner_config),
|
|
||||||
fan_mode=FanModeConfig.from_mara(web_miner_config),
|
|
||||||
mining_mode=MiningModeConfig.from_mara(web_miner_config),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
|
|
||||||
return cls(
|
|
||||||
pools=PoolConfig.from_espminer(web_system_info),
|
|
||||||
fan_mode=FanModeConfig.from_espminer(web_system_info),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_iceriver(cls, web_userpanel: dict) -> "MinerConfig":
|
|
||||||
return cls(
|
|
||||||
pools=PoolConfig.from_iceriver(web_userpanel),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(
|
|
||||||
cls,
|
|
||||||
rpc_tempctrl: dict,
|
|
||||||
rpc_fans: dict,
|
|
||||||
rpc_pools: dict,
|
|
||||||
rpc_groups: dict,
|
|
||||||
rpc_config: dict,
|
|
||||||
rpc_profiles: dict,
|
|
||||||
) -> "MinerConfig":
|
|
||||||
return cls(
|
|
||||||
temperature=TemperatureConfig.from_luxos(rpc_tempctrl=rpc_tempctrl),
|
|
||||||
fan_mode=FanModeConfig.from_luxos(
|
|
||||||
rpc_tempctrl=rpc_tempctrl, rpc_fans=rpc_fans
|
|
||||||
),
|
|
||||||
pools=PoolConfig.from_luxos(rpc_pools=rpc_pools, rpc_groups=rpc_groups),
|
|
||||||
mining_mode=MiningModeConfig.from_luxos(
|
|
||||||
rpc_config=rpc_config, rpc_profiles=rpc_profiles
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
|
|
||||||
return cls.from_am_modern(*args, **kwargs)
|
|
||||||
|
|||||||
@@ -15,10 +15,9 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict, dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class MinerConfigOption(Enum):
|
class MinerConfigOption(Enum):
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -28,9 +27,6 @@ class MinerConfigOption(Enum):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return self.value.as_am_modern()
|
return self.value.as_am_modern()
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
return self.value.as_hiveon_modern()
|
|
||||||
|
|
||||||
def as_am_old(self) -> dict:
|
def as_am_old(self) -> dict:
|
||||||
return self.value.as_am_old()
|
return self.value.as_am_old()
|
||||||
|
|
||||||
@@ -50,7 +46,7 @@ class MinerConfigOption(Enum):
|
|||||||
return self.value.as_bosminer()
|
return self.value.as_bosminer()
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
def as_boser(self) -> dict:
|
||||||
return self.value.as_boser
|
return self.value.as_boser()
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
def as_epic(self) -> dict:
|
||||||
return self.value.as_epic()
|
return self.value.as_epic()
|
||||||
@@ -61,18 +57,6 @@ class MinerConfigOption(Enum):
|
|||||||
def as_auradine(self) -> dict:
|
def as_auradine(self) -> dict:
|
||||||
return self.value.as_auradine()
|
return self.value.as_auradine()
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return self.value.as_mara()
|
|
||||||
|
|
||||||
def as_espminer(self) -> dict:
|
|
||||||
return self.value.as_espminer()
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return self.value.as_luxos()
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return self.value.as_elphapex()
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
return self.value(*args, **kwargs)
|
return self.value(*args, **kwargs)
|
||||||
|
|
||||||
@@ -80,27 +64,19 @@ class MinerConfigOption(Enum):
|
|||||||
def default(cls):
|
def default(cls):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __getitem__(self, item):
|
|
||||||
try:
|
|
||||||
return getattr(self, item)
|
|
||||||
except AttributeError:
|
|
||||||
raise KeyError
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class MinerConfigValue(BaseModel):
|
class MinerConfigValue:
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None):
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
return self.model_dump()
|
return asdict(self)
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_am_old(self) -> dict:
|
def as_am_old(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -130,21 +106,3 @@ class MinerConfigValue(BaseModel):
|
|||||||
|
|
||||||
def as_auradine(self) -> dict:
|
def as_auradine(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_espminer(self) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def __getitem__(self, item):
|
|
||||||
try:
|
|
||||||
return getattr(self, item)
|
|
||||||
except AttributeError:
|
|
||||||
raise KeyError
|
|
||||||
|
|||||||
@@ -15,15 +15,14 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TypeVar, Union
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class FanModeNormal(MinerConfigValue):
|
class FanModeNormal(MinerConfigValue):
|
||||||
mode: str = Field(init=False, default="normal")
|
mode: str = field(init=False, default="normal")
|
||||||
minimum_fans: int = 1
|
minimum_fans: int = 1
|
||||||
minimum_speed: int = 0
|
minimum_speed: int = 0
|
||||||
|
|
||||||
@@ -55,12 +54,6 @@ class FanModeNormal(MinerConfigValue):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"temp_control": {"mode": "auto"},
|
"temp_control": {"mode": "auto"},
|
||||||
@@ -78,36 +71,10 @@ class FanModeNormal(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"general-config": {"environment-profile": "AirCooling"},
|
|
||||||
"advance-config": {
|
|
||||||
"override-fan-control": False,
|
|
||||||
"fan-fixed-percent": 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_espminer(self) -> dict:
|
|
||||||
return {"autoFanspeed": 1}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
return {
|
|
||||||
"cooling": {
|
|
||||||
"fan_min_count": self.minimum_fans,
|
|
||||||
"fan_min_duty": self.minimum_speed,
|
|
||||||
"mode": {
|
|
||||||
"name": "auto",
|
|
||||||
"param": None, # Target temp, must be set later...
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class FanModeManual(MinerConfigValue):
|
class FanModeManual(MinerConfigValue):
|
||||||
mode: str = Field(init=False, default="manual")
|
mode: str = field(init=False, default="manual")
|
||||||
speed: int = 100
|
speed: int = 100
|
||||||
minimum_fans: int = 1
|
minimum_fans: int = 1
|
||||||
|
|
||||||
@@ -141,12 +108,6 @@ class FanModeManual(MinerConfigValue):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"fc-fan-ctrl": True, "fc-fan-pwm": str(self.speed)}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"temp_control": {"mode": "manual"},
|
"temp_control": {"mode": "manual"},
|
||||||
@@ -159,36 +120,10 @@ class FanModeManual(MinerConfigValue):
|
|||||||
def as_epic(self) -> dict:
|
def as_epic(self) -> dict:
|
||||||
return {"fans": {"Manual": {"speed": self.speed}}}
|
return {"fans": {"Manual": {"speed": self.speed}}}
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"general-config": {"environment-profile": "AirCooling"},
|
|
||||||
"advance-config": {
|
|
||||||
"override-fan-control": True,
|
|
||||||
"fan-fixed-percent": self.speed,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_espminer(self) -> dict:
|
|
||||||
return {"autoFanspeed": 0, "fanspeed": self.speed}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
return {
|
|
||||||
"cooling": {
|
|
||||||
"fan_min_count": self.minimum_fans,
|
|
||||||
"fan_min_duty": self.speed,
|
|
||||||
"mode": {
|
|
||||||
"name": "manual",
|
|
||||||
"param": self.speed, # Speed value
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class FanModeImmersion(MinerConfigValue):
|
class FanModeImmersion(MinerConfigValue):
|
||||||
mode: str = Field(init=False, default="immersion")
|
mode: str = field(init=False, default="immersion")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "FanModeImmersion":
|
def from_dict(cls, dict_conf: dict | None) -> "FanModeImmersion":
|
||||||
@@ -197,12 +132,6 @@ class FanModeImmersion(MinerConfigValue):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"fan_control": {"min_fans": 0},
|
"fan_control": {"min_fans": 0},
|
||||||
@@ -211,15 +140,6 @@ class FanModeImmersion(MinerConfigValue):
|
|||||||
def as_auradine(self) -> dict:
|
def as_auradine(self) -> dict:
|
||||||
return {"fan": {"percentage": 0}}
|
return {"fan": {"percentage": 0}}
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {"general-config": {"environment-profile": "OilImmersionCooling"}}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"fanset": {"speed": 0, "min_fans": 0}}
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
return {"cooling": {"mode": {"name": "immers"}}}
|
|
||||||
|
|
||||||
|
|
||||||
class FanModeConfig(MinerConfigOption):
|
class FanModeConfig(MinerConfigOption):
|
||||||
normal = FanModeNormal
|
normal = FanModeNormal
|
||||||
@@ -257,34 +177,6 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
else:
|
else:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hiveon_modern(cls, web_conf: dict):
|
|
||||||
if web_conf.get("bitmain-fan-ctrl") is not None:
|
|
||||||
fan_manual = web_conf["bitmain-fan-ctrl"]
|
|
||||||
if fan_manual:
|
|
||||||
speed = int(web_conf["bitmain-fan-pwm"])
|
|
||||||
if speed == 0:
|
|
||||||
return cls.immersion()
|
|
||||||
return cls.manual(speed=speed)
|
|
||||||
else:
|
|
||||||
return cls.normal()
|
|
||||||
else:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_elphapex(cls, web_conf: dict):
|
|
||||||
if web_conf.get("fc-fan-ctrl") is not None:
|
|
||||||
fan_manual = web_conf["fc-fan-ctrl"]
|
|
||||||
if fan_manual:
|
|
||||||
speed = int(web_conf["fc-fan-pwm"])
|
|
||||||
if speed == 0:
|
|
||||||
return cls.immersion()
|
|
||||||
return cls.manual(speed=speed)
|
|
||||||
else:
|
|
||||||
return cls.normal()
|
|
||||||
else:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict):
|
def from_epic(cls, web_conf: dict):
|
||||||
try:
|
try:
|
||||||
@@ -345,7 +237,7 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
keys = temperature_conf.keys()
|
keys = temperature_conf.keys()
|
||||||
if "auto" in keys:
|
if "auto" in keys:
|
||||||
if "minimumRequiredFans" in keys:
|
if "minimumRequiredFans" in keys:
|
||||||
return cls.normal(minimum_fans=temperature_conf["minimumRequiredFans"])
|
return cls.normal(temperature_conf["minimumRequiredFans"])
|
||||||
return cls.normal()
|
return cls.normal()
|
||||||
if "manual" in keys:
|
if "manual" in keys:
|
||||||
conf = {}
|
conf = {}
|
||||||
@@ -354,12 +246,6 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
if "minimumRequiredFans" in keys:
|
if "minimumRequiredFans" in keys:
|
||||||
conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"])
|
conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"])
|
||||||
return cls.manual(**conf)
|
return cls.manual(**conf)
|
||||||
if "disabled" in keys:
|
|
||||||
conf = {}
|
|
||||||
if "fanSpeedRatio" in temperature_conf["disabled"].keys():
|
|
||||||
conf["speed"] = int(temperature_conf["disabled"]["fanSpeedRatio"])
|
|
||||||
return cls.manual(**conf)
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_auradine(cls, web_fan: dict):
|
def from_auradine(cls, web_fan: dict):
|
||||||
@@ -369,53 +255,4 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
fan_1_target = fan_data["Target"]
|
fan_1_target = fan_data["Target"]
|
||||||
return cls.manual(speed=round((fan_1_target / fan_1_max) * 100))
|
return cls.manual(speed=round((fan_1_target / fan_1_max) * 100))
|
||||||
except LookupError:
|
except LookupError:
|
||||||
pass
|
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mara(cls, web_config: dict):
|
|
||||||
try:
|
|
||||||
mode = web_config["general-config"]["environment-profile"]
|
|
||||||
if mode == "AirCooling":
|
|
||||||
if web_config["advance-config"]["override-fan-control"]:
|
|
||||||
return cls.manual(
|
|
||||||
speed=web_config["advance-config"]["fan-fixed-percent"]
|
|
||||||
)
|
|
||||||
return cls.normal()
|
|
||||||
return cls.immersion()
|
|
||||||
except LookupError:
|
|
||||||
pass
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_espminer(cls, web_system_info: dict):
|
|
||||||
if web_system_info["autofanspeed"] == 1:
|
|
||||||
return cls.normal()
|
|
||||||
else:
|
|
||||||
return cls.manual(speed=web_system_info["fanspeed"])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, rpc_fans: dict, rpc_tempctrl: dict):
|
|
||||||
try:
|
|
||||||
mode = rpc_tempctrl["TEMPCTRL"][0]["Mode"]
|
|
||||||
if mode == "Manual":
|
|
||||||
speed = rpc_fans["FANS"][0]["Speed"]
|
|
||||||
min_fans = rpc_fans["FANCTRL"][0]["MinFans"]
|
|
||||||
if min_fans == 0 and speed == 0:
|
|
||||||
return cls.immersion()
|
|
||||||
return cls.manual(
|
|
||||||
speed=speed,
|
|
||||||
minimum_fans=min_fans,
|
|
||||||
)
|
|
||||||
return cls.normal(
|
|
||||||
minimum_fans=rpc_fans["FANCTRL"][0]["MinFans"],
|
|
||||||
)
|
|
||||||
except LookupError:
|
|
||||||
pass
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
|
|
||||||
FanMode = TypeVar(
|
|
||||||
"FanMode",
|
|
||||||
bound=Union[FanModeNormal, FanModeManual, FanModeImmersion],
|
|
||||||
)
|
|
||||||
|
|||||||
470
pyasic/config/mining.py
Normal file
470
pyasic/config/mining.py
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from pyasic import settings
|
||||||
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
||||||
|
HashrateTargetMode,
|
||||||
|
PerformanceMode,
|
||||||
|
Power,
|
||||||
|
PowerTargetMode,
|
||||||
|
SaveAction,
|
||||||
|
SetPerformanceModeRequest,
|
||||||
|
TeraHashrate,
|
||||||
|
TunerPerformanceMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModeNormal(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="normal")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeNormal":
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": self.mode}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {"ptune": {"enabled": False}}
|
||||||
|
|
||||||
|
def as_goldshell(self) -> dict:
|
||||||
|
return {"settings": {"level": 0}}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModeSleep(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="sleep")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeSleep":
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "1"}
|
||||||
|
return {"miner-mode": 1}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"sleep": "on"}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {"ptune": {"algo": "Sleep", "target": 0}}
|
||||||
|
|
||||||
|
def as_goldshell(self) -> dict:
|
||||||
|
return {"settings": {"level": 3}}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModeLPM(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="low")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeLPM":
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "3"}
|
||||||
|
return {"miner-mode": 3}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "eco"}}
|
||||||
|
|
||||||
|
def as_goldshell(self) -> dict:
|
||||||
|
return {"settings": {"level": 1}}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModeHPM(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="high")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHPM":
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "turbo"}}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StandardTuneAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="standard")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return VOptAlgo().as_epic()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class VOptAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="voltage_optimizer")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return "VoltageOptimizer"
|
||||||
|
|
||||||
|
|
||||||
|
class ChipTuneAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="chip_tune")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return "ChipTune"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TunerAlgo(MinerConfigOption):
|
||||||
|
standard = StandardTuneAlgo
|
||||||
|
voltage_optimizer = VOptAlgo
|
||||||
|
chip_tune = ChipTuneAlgo
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls):
|
||||||
|
return cls.standard()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
|
mode = dict_conf.get("mode")
|
||||||
|
if mode is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
cls_attr = getattr(cls, mode)
|
||||||
|
if cls_attr is not None:
|
||||||
|
return cls_attr().from_dict(dict_conf)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModePowerTune(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="power_tuning")
|
||||||
|
power: int = None
|
||||||
|
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
|
||||||
|
cls_conf = {}
|
||||||
|
if dict_conf.get("power"):
|
||||||
|
cls_conf["power"] = dict_conf["power"]
|
||||||
|
if dict_conf.get("algo"):
|
||||||
|
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
|
||||||
|
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
if self.power is not None:
|
||||||
|
return {"mode": self.mode, self.mode: {"wattage": self.power}}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
conf = {"enabled": True, "mode": "power_target"}
|
||||||
|
if self.power is not None:
|
||||||
|
conf["power_target"] = self.power
|
||||||
|
return {"autotuning": conf}
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
return {
|
||||||
|
"set_performance_mode": SetPerformanceModeRequest(
|
||||||
|
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||||
|
mode=PerformanceMode(
|
||||||
|
tuner_mode=TunerPerformanceMode(
|
||||||
|
power_target=PowerTargetMode(
|
||||||
|
power_target=Power(watt=self.power)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModeHashrateTune(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="hashrate_tuning")
|
||||||
|
hashrate: int = None
|
||||||
|
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
|
||||||
|
cls_conf = {}
|
||||||
|
if dict_conf.get("hashrate"):
|
||||||
|
cls_conf["hashrate"] = dict_conf["hashrate"]
|
||||||
|
if dict_conf.get("algo"):
|
||||||
|
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
|
||||||
|
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
conf = {"enabled": True, "mode": "hashrate_target"}
|
||||||
|
if self.hashrate is not None:
|
||||||
|
conf["hashrate_target"] = self.hashrate
|
||||||
|
return {"autotuning": conf}
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
return {
|
||||||
|
"set_performance_mode": SetPerformanceModeRequest(
|
||||||
|
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||||
|
mode=PerformanceMode(
|
||||||
|
tuner_mode=TunerPerformanceMode(
|
||||||
|
hashrate_target=HashrateTargetMode(
|
||||||
|
hashrate_target=TeraHashrate(
|
||||||
|
terahash_per_second=self.hashrate
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {"ptune": {"algo": self.algo.as_epic(), "target": self.hashrate}}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ManualBoardSettings(MinerConfigValue):
|
||||||
|
freq: float
|
||||||
|
volt: float
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "ManualBoardSettings":
|
||||||
|
return cls(freq=dict_conf["freq"], volt=dict_conf["volt"])
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MiningModeManual(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="manual")
|
||||||
|
|
||||||
|
global_freq: float
|
||||||
|
global_volt: float
|
||||||
|
boards: dict[int, ManualBoardSettings] = field(default_factory=dict)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeManual":
|
||||||
|
return cls(
|
||||||
|
global_freq=dict_conf["global_freq"],
|
||||||
|
global_volt=dict_conf["global_volt"],
|
||||||
|
boards={i: ManualBoardSettings.from_dict(dict_conf[i]) for i in dict_conf},
|
||||||
|
)
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
|
||||||
|
# will raise KeyError if it cant find the settings, values cannot be empty
|
||||||
|
voltage = web_overclock_settings["globals"]["volt"]
|
||||||
|
freq = web_overclock_settings["globals"]["freq"]
|
||||||
|
boards = {
|
||||||
|
idx: ManualBoardSettings(
|
||||||
|
freq=board["freq"],
|
||||||
|
volt=voltage if not board["freq"] == 0 else 0,
|
||||||
|
)
|
||||||
|
for idx, board in enumerate(web_overclock_settings["chains"])
|
||||||
|
}
|
||||||
|
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeConfig(MinerConfigOption):
|
||||||
|
normal = MiningModeNormal
|
||||||
|
low = MiningModeLPM
|
||||||
|
high = MiningModeHPM
|
||||||
|
sleep = MiningModeSleep
|
||||||
|
power_tuning = MiningModePowerTune
|
||||||
|
hashrate_tuning = MiningModeHashrateTune
|
||||||
|
manual = MiningModeManual
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls):
|
||||||
|
return cls.normal()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
mode = dict_conf.get("mode")
|
||||||
|
if mode is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
cls_attr = getattr(cls, mode)
|
||||||
|
if cls_attr is not None:
|
||||||
|
return cls_attr().from_dict(dict_conf)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_am_modern(cls, web_conf: dict):
|
||||||
|
if web_conf.get("bitmain-work-mode") is not None:
|
||||||
|
work_mode = web_conf["bitmain-work-mode"]
|
||||||
|
if work_mode == "":
|
||||||
|
return cls.default()
|
||||||
|
if int(work_mode) == 0:
|
||||||
|
return cls.normal()
|
||||||
|
elif int(work_mode) == 1:
|
||||||
|
return cls.sleep()
|
||||||
|
elif int(work_mode) == 3:
|
||||||
|
return cls.low()
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_epic(cls, web_conf: dict):
|
||||||
|
try:
|
||||||
|
tuner_running = web_conf["PerpetualTune"]["Running"]
|
||||||
|
if tuner_running:
|
||||||
|
algo_info = web_conf["PerpetualTune"]["Algorithm"]
|
||||||
|
if algo_info.get("VoltageOptimizer") is not None:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=algo_info["VoltageOptimizer"]["Target"],
|
||||||
|
algo=TunerAlgo.voltage_optimizer,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=algo_info["ChipTune"]["Target"],
|
||||||
|
algo=TunerAlgo.chip_tune,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return cls.normal()
|
||||||
|
except KeyError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_conf: dict):
|
||||||
|
if toml_conf.get("autotuning") is None:
|
||||||
|
return cls.default()
|
||||||
|
autotuning_conf = toml_conf["autotuning"]
|
||||||
|
|
||||||
|
if autotuning_conf.get("enabled") is None:
|
||||||
|
return cls.default()
|
||||||
|
if not autotuning_conf["enabled"]:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
if autotuning_conf.get("psu_power_limit") is not None:
|
||||||
|
# old autotuning conf
|
||||||
|
return cls.power_tuning(autotuning_conf["psu_power_limit"])
|
||||||
|
if autotuning_conf.get("mode") is not None:
|
||||||
|
# new autotuning conf
|
||||||
|
mode = autotuning_conf["mode"]
|
||||||
|
if mode == "power_target":
|
||||||
|
if autotuning_conf.get("power_target") is not None:
|
||||||
|
return cls.power_tuning(autotuning_conf["power_target"])
|
||||||
|
return cls.power_tuning()
|
||||||
|
if mode == "hashrate_target":
|
||||||
|
if autotuning_conf.get("hashrate_target") is not None:
|
||||||
|
return cls.hashrate_tuning(autotuning_conf["hashrate_target"])
|
||||||
|
return cls.hashrate_tuning()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vnish(cls, web_settings: dict):
|
||||||
|
try:
|
||||||
|
mode_settings = web_settings["miner"]["overclock"]
|
||||||
|
except KeyError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
if mode_settings["preset"] == "disabled":
|
||||||
|
return MiningModeManual.from_vnish(mode_settings)
|
||||||
|
else:
|
||||||
|
return cls.power_tuning(int(mode_settings["preset"]))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, grpc_miner_conf: dict):
|
||||||
|
try:
|
||||||
|
tuner_conf = grpc_miner_conf["tuner"]
|
||||||
|
if not tuner_conf.get("enabled", False):
|
||||||
|
return cls.default()
|
||||||
|
except LookupError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
if tuner_conf.get("tunerMode") is not None:
|
||||||
|
if tuner_conf["tunerMode"] == 1:
|
||||||
|
if tuner_conf.get("powerTarget") is not None:
|
||||||
|
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
|
||||||
|
return cls.power_tuning()
|
||||||
|
|
||||||
|
if tuner_conf["tunerMode"] == 2:
|
||||||
|
if tuner_conf.get("hashrateTarget") is not None:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
|
||||||
|
)
|
||||||
|
return cls.hashrate_tuning()
|
||||||
|
|
||||||
|
if tuner_conf.get("powerTarget") is not None:
|
||||||
|
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
|
||||||
|
|
||||||
|
if tuner_conf.get("hashrateTarget") is not None:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_auradine(cls, web_mode: dict):
|
||||||
|
try:
|
||||||
|
mode_data = web_mode["Mode"][0]
|
||||||
|
if mode_data.get("Sleep") == "on":
|
||||||
|
return cls.sleep()
|
||||||
|
if mode_data.get("Mode") == "normal":
|
||||||
|
return cls.normal()
|
||||||
|
if mode_data.get("Mode") == "eco":
|
||||||
|
return cls.low()
|
||||||
|
if mode_data.get("Mode") == "turbo":
|
||||||
|
return cls.high()
|
||||||
|
if mode_data.get("Ths") is not None:
|
||||||
|
return cls.hashrate_tuning(mode_data["Ths"])
|
||||||
|
if mode_data.get("Power") is not None:
|
||||||
|
return cls.power_tuning(mode_data["Power"])
|
||||||
|
except LookupError:
|
||||||
|
return cls.default()
|
||||||
@@ -1,828 +0,0 @@
|
|||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Copyright 2022 Upstream Data Inc -
|
|
||||||
# -
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
|
||||||
# you may not use this file except in compliance with the License. -
|
|
||||||
# You may obtain a copy of the License at -
|
|
||||||
# -
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
|
||||||
# -
|
|
||||||
# Unless required by applicable law or agreed to in writing, software -
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
|
||||||
# See the License for the specific language governing permissions and -
|
|
||||||
# limitations under the License. -
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import field
|
|
||||||
from typing import TypeVar, Union
|
|
||||||
|
|
||||||
from pyasic import settings
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
|
||||||
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
|
||||||
DpsHashrateTarget,
|
|
||||||
DpsPowerTarget,
|
|
||||||
DpsTarget,
|
|
||||||
HashrateTargetMode,
|
|
||||||
PerformanceMode,
|
|
||||||
Power,
|
|
||||||
PowerTargetMode,
|
|
||||||
SaveAction,
|
|
||||||
SetDpsRequest,
|
|
||||||
SetPerformanceModeRequest,
|
|
||||||
TeraHashrate,
|
|
||||||
TunerPerformanceMode,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .algo import TunerAlgo, TunerAlgoType
|
|
||||||
from .presets import MiningPreset
|
|
||||||
from .scaling import ScalingConfig
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeNormal(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="normal")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeNormal":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
def as_auradine(self) -> dict:
|
|
||||||
return {"mode": {"mode": self.mode}}
|
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
|
||||||
return {"ptune": {"enabled": False}}
|
|
||||||
|
|
||||||
def as_goldshell(self) -> dict:
|
|
||||||
return {"settings": {"level": 0}}
|
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"mode": {
|
|
||||||
"work-mode-selector": "Stock",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"autotunerset": {"enabled": False}}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
return {"autotuning": {"enabled": True}}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeSleep(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="sleep")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeSleep":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "1"}
|
|
||||||
return {"miner-mode": 1}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "1"}
|
|
||||||
return {"miner-mode": 1}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 1}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
def as_auradine(self) -> dict:
|
|
||||||
return {"mode": {"sleep": "on"}}
|
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
|
||||||
return {"ptune": {"algo": "Sleep", "target": 0}}
|
|
||||||
|
|
||||||
def as_goldshell(self) -> dict:
|
|
||||||
return {"settings": {"level": 3}}
|
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"mode": {
|
|
||||||
"work-mode-selector": "Sleep",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeLPM(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="low")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeLPM":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "3"}
|
|
||||||
return {"miner-mode": 3}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "3"}
|
|
||||||
return {"miner-mode": 3}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 3}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
def as_auradine(self) -> dict:
|
|
||||||
return {"mode": {"mode": "eco"}}
|
|
||||||
|
|
||||||
def as_goldshell(self) -> dict:
|
|
||||||
return {"settings": {"level": 1}}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeHPM(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="high")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHPM":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
def as_auradine(self) -> dict:
|
|
||||||
return {"mode": {"mode": "turbo"}}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModePowerTune(MinerConfigValue):
|
|
||||||
class Config:
|
|
||||||
arbitrary_types_allowed = True
|
|
||||||
|
|
||||||
mode: str = field(init=False, default="power_tuning")
|
|
||||||
power: int | None = None
|
|
||||||
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
|
|
||||||
scaling: ScalingConfig | None = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
|
|
||||||
cls_conf = {}
|
|
||||||
if dict_conf.get("power"):
|
|
||||||
cls_conf["power"] = dict_conf["power"]
|
|
||||||
if dict_conf.get("algo"):
|
|
||||||
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
|
|
||||||
if dict_conf.get("scaling"):
|
|
||||||
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
|
|
||||||
|
|
||||||
return cls(**cls_conf)
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
if self.power is not None:
|
|
||||||
return {"mode": self.mode, self.mode: {"wattage": self.power}}
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
tuning_cfg = {"enabled": True, "mode": "power_target"}
|
|
||||||
if self.power is not None:
|
|
||||||
tuning_cfg["power_target"] = self.power
|
|
||||||
|
|
||||||
cfg = {"autotuning": tuning_cfg}
|
|
||||||
|
|
||||||
if self.scaling is not None:
|
|
||||||
scaling_cfg = {"enabled": True}
|
|
||||||
if self.scaling.step is not None:
|
|
||||||
scaling_cfg["power_step"] = self.scaling.step
|
|
||||||
if self.scaling.minimum is not None:
|
|
||||||
scaling_cfg["min_power_target"] = self.scaling.minimum
|
|
||||||
if self.scaling.shutdown is not None:
|
|
||||||
scaling_cfg = {**scaling_cfg, **self.scaling.shutdown.as_bosminer()}
|
|
||||||
cfg["performance_scaling"] = scaling_cfg
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
cfg = {
|
|
||||||
"set_performance_mode": SetPerformanceModeRequest(
|
|
||||||
save_action=SaveAction.SAVE_AND_APPLY,
|
|
||||||
mode=PerformanceMode(
|
|
||||||
tuner_mode=TunerPerformanceMode(
|
|
||||||
power_target=PowerTargetMode(
|
|
||||||
power_target=Power(watt=self.power)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
if self.scaling is not None:
|
|
||||||
sd_cfg = {}
|
|
||||||
if self.scaling.shutdown is not None:
|
|
||||||
sd_cfg = self.scaling.shutdown.as_boser()
|
|
||||||
power_target_kwargs = {}
|
|
||||||
if self.scaling.step is not None:
|
|
||||||
power_target_kwargs["power_step"] = Power(self.scaling.step)
|
|
||||||
if self.scaling.minimum is not None:
|
|
||||||
power_target_kwargs["min_power_target"] = Power(self.scaling.minimum)
|
|
||||||
cfg["set_dps"] = SetDpsRequest(
|
|
||||||
save_action=SaveAction.SAVE_AND_APPLY,
|
|
||||||
enable=True,
|
|
||||||
**sd_cfg,
|
|
||||||
target=DpsTarget(power_target=DpsPowerTarget(**power_target_kwargs)),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
def as_auradine(self) -> dict:
|
|
||||||
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
|
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"mode": {
|
|
||||||
"work-mode-selector": "Auto",
|
|
||||||
"concorde": {
|
|
||||||
"mode-select": "PowerTarget",
|
|
||||||
"power-target": self.power,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"autotunerset": {"enabled": True}}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeHashrateTune(MinerConfigValue):
|
|
||||||
class Config:
|
|
||||||
arbitrary_types_allowed = True
|
|
||||||
|
|
||||||
mode: str = field(init=False, default="hashrate_tuning")
|
|
||||||
hashrate: int | None = None
|
|
||||||
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
|
|
||||||
scaling: ScalingConfig | None = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
|
|
||||||
cls_conf = {}
|
|
||||||
if dict_conf.get("hashrate"):
|
|
||||||
cls_conf["hashrate"] = dict_conf["hashrate"]
|
|
||||||
if dict_conf.get("algo"):
|
|
||||||
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
|
|
||||||
if dict_conf.get("scaling"):
|
|
||||||
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
|
|
||||||
|
|
||||||
return cls(**cls_conf)
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
conf = {"enabled": True, "mode": "hashrate_target"}
|
|
||||||
if self.hashrate is not None:
|
|
||||||
conf["hashrate_target"] = self.hashrate
|
|
||||||
return {"autotuning": conf}
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
cfg = {
|
|
||||||
"set_performance_mode": SetPerformanceModeRequest(
|
|
||||||
save_action=SaveAction.SAVE_AND_APPLY,
|
|
||||||
mode=PerformanceMode(
|
|
||||||
tuner_mode=TunerPerformanceMode(
|
|
||||||
hashrate_target=HashrateTargetMode(
|
|
||||||
hashrate_target=TeraHashrate(
|
|
||||||
terahash_per_second=self.hashrate
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if self.scaling is not None:
|
|
||||||
sd_cfg = {}
|
|
||||||
if self.scaling.shutdown is not None:
|
|
||||||
sd_cfg = self.scaling.shutdown.as_boser()
|
|
||||||
hashrate_target_kwargs = {}
|
|
||||||
if self.scaling.step is not None:
|
|
||||||
hashrate_target_kwargs["hashrate_step"] = TeraHashrate(
|
|
||||||
self.scaling.step
|
|
||||||
)
|
|
||||||
if self.scaling.minimum is not None:
|
|
||||||
hashrate_target_kwargs["min_hashrate_target"] = TeraHashrate(
|
|
||||||
self.scaling.minimum
|
|
||||||
)
|
|
||||||
cfg["set_dps"] = SetDpsRequest(
|
|
||||||
save_action=SaveAction.SAVE_AND_APPLY,
|
|
||||||
enable=True,
|
|
||||||
**sd_cfg,
|
|
||||||
target=DpsTarget(
|
|
||||||
hashrate_target=DpsHashrateTarget(**hashrate_target_kwargs)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
def as_auradine(self) -> dict:
|
|
||||||
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
|
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
|
||||||
mode = {
|
|
||||||
"ptune": {
|
|
||||||
"algo": self.algo.as_epic(),
|
|
||||||
"target": self.hashrate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.scaling is not None:
|
|
||||||
if self.scaling.minimum is not None:
|
|
||||||
mode["ptune"]["min_throttle"] = self.scaling.minimum
|
|
||||||
if self.scaling.step is not None:
|
|
||||||
mode["ptune"]["throttle_step"] = self.scaling.step
|
|
||||||
return mode
|
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"mode": {
|
|
||||||
"work-mode-selector": "Auto",
|
|
||||||
"concorde": {
|
|
||||||
"mode-select": "Hashrate",
|
|
||||||
"hash-target": self.hashrate,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"autotunerset": {"enabled": True}}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModePreset(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="preset")
|
|
||||||
|
|
||||||
active_preset: MiningPreset
|
|
||||||
available_presets: list[MiningPreset] = field(default_factory=list)
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
return {"overclock": {**self.active_preset.as_vnish()}}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vnish(
|
|
||||||
cls, web_overclock_settings: dict, web_presets: list[dict]
|
|
||||||
) -> "MiningModePreset":
|
|
||||||
active_preset = None
|
|
||||||
for preset in web_presets:
|
|
||||||
if preset["name"] == web_overclock_settings["preset"]:
|
|
||||||
active_preset = preset
|
|
||||||
return cls(
|
|
||||||
active_preset=MiningPreset.from_vnish(active_preset),
|
|
||||||
available_presets=[MiningPreset.from_vnish(p) for p in web_presets],
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> "MiningModePreset":
|
|
||||||
active_preset = cls.get_active_preset_from_luxos(rpc_config, rpc_profiles)
|
|
||||||
return cls(
|
|
||||||
active_preset=active_preset,
|
|
||||||
available_presets=[
|
|
||||||
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_active_preset_from_luxos(
|
|
||||||
cls, rpc_config: dict, rpc_profiles: dict
|
|
||||||
) -> MiningPreset:
|
|
||||||
active_preset = None
|
|
||||||
active_profile = rpc_config["CONFIG"][0]["Profile"]
|
|
||||||
for profile in rpc_profiles["PROFILES"]:
|
|
||||||
if profile["Profile Name"] == active_profile:
|
|
||||||
active_preset = profile
|
|
||||||
return MiningPreset.from_luxos(active_preset)
|
|
||||||
|
|
||||||
|
|
||||||
class ManualBoardSettings(MinerConfigValue):
|
|
||||||
freq: float
|
|
||||||
volt: float
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "ManualBoardSettings":
|
|
||||||
return cls(freq=dict_conf["freq"], volt=dict_conf["volt"])
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_hiveon_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
return {"freq": self.freq}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeManual(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="manual")
|
|
||||||
|
|
||||||
global_freq: float
|
|
||||||
global_volt: float
|
|
||||||
boards: dict[int, ManualBoardSettings] = field(default_factory=dict)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeManual":
|
|
||||||
return cls(
|
|
||||||
global_freq=dict_conf["global_freq"],
|
|
||||||
global_volt=dict_conf["global_volt"],
|
|
||||||
boards={i: ManualBoardSettings.from_dict(dict_conf[i]) for i in dict_conf},
|
|
||||||
)
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
if settings.get("antminer_mining_mode_as_str", False):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_elphapex(self) -> dict:
|
|
||||||
return {"miner-mode": 0}
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
|
|
||||||
return {
|
|
||||||
"overclock": {
|
|
||||||
"chains": chains if chains != [] else None,
|
|
||||||
"globals": {
|
|
||||||
"freq": int(self.global_freq),
|
|
||||||
"volt": int(self.global_volt),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
|
|
||||||
# will raise KeyError if it cant find the settings, values cannot be empty
|
|
||||||
voltage = web_overclock_settings["globals"]["volt"]
|
|
||||||
freq = web_overclock_settings["globals"]["freq"]
|
|
||||||
boards = {
|
|
||||||
idx: ManualBoardSettings(
|
|
||||||
freq=board["freq"],
|
|
||||||
volt=voltage if not board["freq"] == 0 else 0,
|
|
||||||
)
|
|
||||||
for idx, board in enumerate(web_overclock_settings["chains"])
|
|
||||||
}
|
|
||||||
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_epic(cls, epic_conf: dict) -> "MiningModeManual":
|
|
||||||
voltage = 0
|
|
||||||
freq = 0
|
|
||||||
if epic_conf.get("HwConfig") is not None:
|
|
||||||
freq = epic_conf["HwConfig"]["Boards Target Clock"][0]["Data"]
|
|
||||||
if epic_conf.get("Power Supply Stats") is not None:
|
|
||||||
voltage = epic_conf["Power Supply Stats"]["Target Voltage"]
|
|
||||||
boards = {}
|
|
||||||
if epic_conf.get("HBs") is not None:
|
|
||||||
boards = {
|
|
||||||
board["Index"]: ManualBoardSettings(
|
|
||||||
freq=board["Core Clock Avg"], volt=board["Input Voltage"]
|
|
||||||
)
|
|
||||||
for board in epic_conf["HBs"]
|
|
||||||
}
|
|
||||||
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
|
||||||
|
|
||||||
def as_mara(self) -> dict:
|
|
||||||
return {
|
|
||||||
"mode": {
|
|
||||||
"work-mode-selector": "Fixed",
|
|
||||||
"fixed": {
|
|
||||||
"frequency": str(self.global_freq),
|
|
||||||
"voltage": self.global_volt,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeConfig(MinerConfigOption):
|
|
||||||
normal = MiningModeNormal
|
|
||||||
low = MiningModeLPM
|
|
||||||
high = MiningModeHPM
|
|
||||||
sleep = MiningModeSleep
|
|
||||||
power_tuning = MiningModePowerTune
|
|
||||||
hashrate_tuning = MiningModeHashrateTune
|
|
||||||
preset = MiningModePreset
|
|
||||||
manual = MiningModeManual
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def default(cls):
|
|
||||||
return cls.normal()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None):
|
|
||||||
if dict_conf is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
mode = dict_conf.get("mode")
|
|
||||||
if mode is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
cls_attr = getattr(cls, mode)
|
|
||||||
if cls_attr is not None:
|
|
||||||
return cls_attr().from_dict(dict_conf)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_am_modern(cls, web_conf: dict):
|
|
||||||
if web_conf.get("bitmain-work-mode") is not None:
|
|
||||||
work_mode = web_conf["bitmain-work-mode"]
|
|
||||||
if work_mode == "":
|
|
||||||
return cls.default()
|
|
||||||
if int(work_mode) == 0:
|
|
||||||
return cls.normal()
|
|
||||||
elif int(work_mode) == 1:
|
|
||||||
return cls.sleep()
|
|
||||||
elif int(work_mode) == 3:
|
|
||||||
return cls.low()
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hiveon_modern(cls, web_conf: dict):
|
|
||||||
if web_conf.get("bitmain-work-mode") is not None:
|
|
||||||
work_mode = web_conf["bitmain-work-mode"]
|
|
||||||
if work_mode == "":
|
|
||||||
return cls.default()
|
|
||||||
if int(work_mode) == 0:
|
|
||||||
return cls.normal()
|
|
||||||
elif int(work_mode) == 1:
|
|
||||||
return cls.sleep()
|
|
||||||
elif int(work_mode) == 3:
|
|
||||||
return cls.low()
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_elphapex(cls, web_conf: dict):
|
|
||||||
if web_conf.get("fc-work-mode") is not None:
|
|
||||||
work_mode = web_conf["fc-work-mode"]
|
|
||||||
if work_mode == "":
|
|
||||||
return cls.default()
|
|
||||||
if int(work_mode) == 0:
|
|
||||||
return cls.normal()
|
|
||||||
elif int(work_mode) == 1:
|
|
||||||
return cls.sleep()
|
|
||||||
elif int(work_mode) == 3:
|
|
||||||
return cls.low()
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_epic(cls, web_conf: dict):
|
|
||||||
try:
|
|
||||||
tuner_running = web_conf["PerpetualTune"]["Running"]
|
|
||||||
if tuner_running:
|
|
||||||
algo_info = web_conf["PerpetualTune"]["Algorithm"]
|
|
||||||
if algo_info.get("VoltageOptimizer") is not None:
|
|
||||||
scaling_cfg = None
|
|
||||||
if "Throttle Step" in algo_info["VoltageOptimizer"]:
|
|
||||||
scaling_cfg = ScalingConfig(
|
|
||||||
minimum=algo_info["VoltageOptimizer"].get(
|
|
||||||
"Min Throttle Target"
|
|
||||||
),
|
|
||||||
step=algo_info["VoltageOptimizer"].get("Throttle Step"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
hashrate=algo_info["VoltageOptimizer"].get("Target"),
|
|
||||||
algo=TunerAlgo.voltage_optimizer(),
|
|
||||||
scaling=scaling_cfg,
|
|
||||||
)
|
|
||||||
elif algo_info.get("BoardTune") is not None:
|
|
||||||
scaling_cfg = None
|
|
||||||
if "Throttle Step" in algo_info["BoardTune"]:
|
|
||||||
scaling_cfg = ScalingConfig(
|
|
||||||
minimum=algo_info["BoardTune"].get("Min Throttle Target"),
|
|
||||||
step=algo_info["BoardTune"].get("Throttle Step"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
hashrate=algo_info["BoardTune"].get("Target"),
|
|
||||||
algo=TunerAlgo.board_tune(),
|
|
||||||
scaling=scaling_cfg,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
hashrate=algo_info["ChipTune"].get("Target"),
|
|
||||||
algo=TunerAlgo.chip_tune(),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return MiningModeManual.from_epic(web_conf)
|
|
||||||
except KeyError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, toml_conf: dict):
|
|
||||||
if toml_conf.get("autotuning") is None:
|
|
||||||
return cls.default()
|
|
||||||
autotuning_conf = toml_conf["autotuning"]
|
|
||||||
|
|
||||||
if autotuning_conf.get("enabled") is None:
|
|
||||||
return cls.default()
|
|
||||||
if not autotuning_conf["enabled"]:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
if autotuning_conf.get("psu_power_limit") is not None:
|
|
||||||
# old autotuning conf
|
|
||||||
return cls.power_tuning(
|
|
||||||
power=autotuning_conf["psu_power_limit"],
|
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
|
||||||
)
|
|
||||||
if autotuning_conf.get("mode") is not None:
|
|
||||||
# new autotuning conf
|
|
||||||
mode = autotuning_conf["mode"]
|
|
||||||
if mode == "power_target":
|
|
||||||
if autotuning_conf.get("power_target") is not None:
|
|
||||||
return cls.power_tuning(
|
|
||||||
power=autotuning_conf["power_target"],
|
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
|
||||||
)
|
|
||||||
return cls.power_tuning(
|
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
|
||||||
)
|
|
||||||
if mode == "hashrate_target":
|
|
||||||
if autotuning_conf.get("hashrate_target") is not None:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
hashrate=autotuning_conf["hashrate_target"],
|
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
|
|
||||||
)
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
|
|
||||||
)
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vnish(cls, web_settings: dict, web_presets: list[dict]):
|
|
||||||
try:
|
|
||||||
mode_settings = web_settings["miner"]["overclock"]
|
|
||||||
except KeyError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
if mode_settings["preset"] == "disabled":
|
|
||||||
return MiningModeManual.from_vnish(mode_settings)
|
|
||||||
else:
|
|
||||||
return MiningModePreset.from_vnish(mode_settings, web_presets)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_boser(cls, grpc_miner_conf: dict):
|
|
||||||
try:
|
|
||||||
tuner_conf = grpc_miner_conf["tuner"]
|
|
||||||
if not tuner_conf.get("enabled", False):
|
|
||||||
return cls.default()
|
|
||||||
except LookupError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
if tuner_conf.get("tunerMode") is not None:
|
|
||||||
if tuner_conf["tunerMode"] == 1:
|
|
||||||
if tuner_conf.get("powerTarget") is not None:
|
|
||||||
return cls.power_tuning(
|
|
||||||
power=tuner_conf["powerTarget"]["watt"],
|
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
|
||||||
)
|
|
||||||
return cls.power_tuning(
|
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power")
|
|
||||||
)
|
|
||||||
|
|
||||||
if tuner_conf["tunerMode"] == 2:
|
|
||||||
if tuner_conf.get("hashrateTarget") is not None:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
|
||||||
scaling=ScalingConfig.from_boser(
|
|
||||||
grpc_miner_conf, mode="hashrate"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if tuner_conf.get("powerTarget") is not None:
|
|
||||||
return cls.power_tuning(
|
|
||||||
power=tuner_conf["powerTarget"]["watt"],
|
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if tuner_conf.get("hashrateTarget") is not None:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_auradine(cls, web_mode: dict):
|
|
||||||
try:
|
|
||||||
mode_data = web_mode["Mode"][0]
|
|
||||||
if mode_data.get("Sleep") == "on":
|
|
||||||
return cls.sleep()
|
|
||||||
if mode_data.get("Mode") == "normal":
|
|
||||||
return cls.normal()
|
|
||||||
if mode_data.get("Mode") == "eco":
|
|
||||||
return cls.low()
|
|
||||||
if mode_data.get("Mode") == "turbo":
|
|
||||||
return cls.high()
|
|
||||||
if mode_data.get("Ths") is not None:
|
|
||||||
return cls.hashrate_tuning(hashrate=mode_data["Ths"])
|
|
||||||
if mode_data.get("Power") is not None:
|
|
||||||
return cls.power_tuning(power=mode_data["Power"])
|
|
||||||
except LookupError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mara(cls, web_config: dict):
|
|
||||||
try:
|
|
||||||
mode = web_config["mode"]["work-mode-selector"]
|
|
||||||
if mode == "Fixed":
|
|
||||||
fixed_conf = web_config["mode"]["fixed"]
|
|
||||||
return cls.manual(
|
|
||||||
global_freq=int(fixed_conf["frequency"]),
|
|
||||||
global_volt=fixed_conf["voltage"],
|
|
||||||
)
|
|
||||||
elif mode == "Stock":
|
|
||||||
return cls.normal()
|
|
||||||
elif mode == "Sleep":
|
|
||||||
return cls.sleep()
|
|
||||||
elif mode == "Auto":
|
|
||||||
auto_conf = web_config["mode"]["concorde"]
|
|
||||||
auto_mode = auto_conf["mode-select"]
|
|
||||||
if auto_mode == "Hashrate":
|
|
||||||
return cls.hashrate_tuning(hashrate=auto_conf["hash-target"])
|
|
||||||
elif auto_mode == "PowerTarget":
|
|
||||||
return cls.power_tuning(power=auto_conf["power-target"])
|
|
||||||
except LookupError:
|
|
||||||
pass
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict):
|
|
||||||
preset_info = MiningModePreset.from_luxos(rpc_config, rpc_profiles)
|
|
||||||
return cls.preset(
|
|
||||||
active_preset=preset_info.active_preset,
|
|
||||||
available_presets=preset_info.available_presets,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
MiningMode = TypeVar(
|
|
||||||
"MiningMode",
|
|
||||||
bound=Union[
|
|
||||||
MiningModeNormal,
|
|
||||||
MiningModeHPM,
|
|
||||||
MiningModeLPM,
|
|
||||||
MiningModeSleep,
|
|
||||||
MiningModeManual,
|
|
||||||
MiningModePowerTune,
|
|
||||||
MiningModeHashrateTune,
|
|
||||||
MiningModePreset,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import field
|
|
||||||
from typing import TypeVar, Union
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
|
||||||
|
|
||||||
|
|
||||||
class StandardTuneAlgo(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="standard")
|
|
||||||
|
|
||||||
def as_epic(self) -> str:
|
|
||||||
return VOptAlgo().as_epic()
|
|
||||||
|
|
||||||
|
|
||||||
class VOptAlgo(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="voltage_optimizer")
|
|
||||||
|
|
||||||
def as_epic(self) -> str:
|
|
||||||
return "VoltageOptimizer"
|
|
||||||
|
|
||||||
|
|
||||||
class BoardTuneAlgo(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="board_tune")
|
|
||||||
|
|
||||||
def as_epic(self) -> str:
|
|
||||||
return "BoardTune"
|
|
||||||
|
|
||||||
|
|
||||||
class ChipTuneAlgo(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="chip_tune")
|
|
||||||
|
|
||||||
def as_epic(self) -> str:
|
|
||||||
return "ChipTune"
|
|
||||||
|
|
||||||
|
|
||||||
class TunerAlgo(MinerConfigOption):
|
|
||||||
standard = StandardTuneAlgo
|
|
||||||
voltage_optimizer = VOptAlgo
|
|
||||||
board_tune = BoardTuneAlgo
|
|
||||||
chip_tune = ChipTuneAlgo
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def default(cls) -> TunerAlgoType:
|
|
||||||
return cls.standard()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> TunerAlgoType:
|
|
||||||
mode = dict_conf.get("mode")
|
|
||||||
if mode is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
cls_attr = getattr(cls, mode)
|
|
||||||
if cls_attr is not None:
|
|
||||||
return cls_attr().from_dict(dict_conf)
|
|
||||||
|
|
||||||
|
|
||||||
TunerAlgoType = TypeVar(
|
|
||||||
"TunerAlgoType",
|
|
||||||
bound=Union[
|
|
||||||
StandardTuneAlgo,
|
|
||||||
VOptAlgo,
|
|
||||||
BoardTuneAlgo,
|
|
||||||
ChipTuneAlgo,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
from pyasic.config.base import MinerConfigValue
|
|
||||||
|
|
||||||
|
|
||||||
class MiningPreset(MinerConfigValue):
|
|
||||||
name: str | None = None
|
|
||||||
power: int | None = None
|
|
||||||
hashrate: int | None = None
|
|
||||||
tuned: bool | None = None
|
|
||||||
modded_psu: bool | None = None
|
|
||||||
frequency: int | None = None
|
|
||||||
voltage: float | None = None
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
if self.name is not None:
|
|
||||||
return {"preset": self.name}
|
|
||||||
return {}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vnish(cls, web_preset: dict):
|
|
||||||
name = web_preset["name"]
|
|
||||||
hr_power_split = web_preset["pretty"].split("~")
|
|
||||||
if len(hr_power_split) == 1:
|
|
||||||
power = None
|
|
||||||
hashrate = None
|
|
||||||
else:
|
|
||||||
power = hr_power_split[0].replace("watt", "").strip()
|
|
||||||
hashrate = (
|
|
||||||
hr_power_split[1]
|
|
||||||
.replace("TH", "")
|
|
||||||
.replace("GH", "")
|
|
||||||
.replace("MH", "")
|
|
||||||
.replace(" LC", "")
|
|
||||||
.strip()
|
|
||||||
)
|
|
||||||
tuned = web_preset["status"] == "tuned"
|
|
||||||
modded_psu = web_preset["modded_psu_required"]
|
|
||||||
return cls(
|
|
||||||
name=name,
|
|
||||||
power=power,
|
|
||||||
hashrate=hashrate,
|
|
||||||
tuned=tuned,
|
|
||||||
modded_psu=modded_psu,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, profile: dict):
|
|
||||||
return cls(
|
|
||||||
name=profile["Profile Name"],
|
|
||||||
power=profile["Watts"],
|
|
||||||
hashrate=round(profile["Hashrate"]),
|
|
||||||
tuned=profile["IsTuned"],
|
|
||||||
frequency=profile["Frequency"],
|
|
||||||
voltage=profile["Voltage"],
|
|
||||||
)
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Copyright 2022 Upstream Data Inc -
|
|
||||||
# -
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
|
||||||
# you may not use this file except in compliance with the License. -
|
|
||||||
# You may obtain a copy of the License at -
|
|
||||||
# -
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
|
||||||
# -
|
|
||||||
# Unless required by applicable law or agreed to in writing, software -
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
|
||||||
# See the License for the specific language governing permissions and -
|
|
||||||
# limitations under the License. -
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigValue
|
|
||||||
|
|
||||||
|
|
||||||
class ScalingShutdown(MinerConfigValue):
|
|
||||||
enabled: bool = False
|
|
||||||
duration: int | None = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown":
|
|
||||||
return cls(
|
|
||||||
enabled=dict_conf.get("enabled", False), duration=dict_conf.get("duration")
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, power_scaling_conf: dict):
|
|
||||||
sd_enabled = power_scaling_conf.get("shutdown_enabled")
|
|
||||||
if sd_enabled is not None:
|
|
||||||
return cls(
|
|
||||||
enabled=sd_enabled, duration=power_scaling_conf.get("shutdown_duration")
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_boser(cls, power_scaling_conf: dict):
|
|
||||||
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
|
||||||
if sd_enabled is not None:
|
|
||||||
try:
|
|
||||||
return cls(
|
|
||||||
enabled=sd_enabled,
|
|
||||||
duration=power_scaling_conf["shutdownDuration"]["hours"],
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
return cls(enabled=sd_enabled)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
cfg = {"shutdown_enabled": self.enabled}
|
|
||||||
|
|
||||||
if self.duration is not None:
|
|
||||||
cfg["shutdown_duration"] = self.duration
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
|
|
||||||
|
|
||||||
|
|
||||||
class ScalingConfig(MinerConfigValue):
|
|
||||||
step: int | None = None
|
|
||||||
minimum: int | None = None
|
|
||||||
shutdown: ScalingShutdown | None = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig":
|
|
||||||
cls_conf = {
|
|
||||||
"step": dict_conf.get("step"),
|
|
||||||
"minimum": dict_conf.get("minimum"),
|
|
||||||
}
|
|
||||||
shutdown = dict_conf.get("shutdown")
|
|
||||||
if shutdown is not None:
|
|
||||||
cls_conf["shutdown"] = ScalingShutdown.from_dict(shutdown)
|
|
||||||
return cls(**cls_conf)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, toml_conf: dict, mode: str | None = None):
|
|
||||||
if mode == "power":
|
|
||||||
return cls._from_bosminer_power(toml_conf)
|
|
||||||
if mode == "hashrate":
|
|
||||||
# not implemented yet
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _from_bosminer_power(cls, toml_conf: dict):
|
|
||||||
power_scaling = toml_conf.get("power_scaling")
|
|
||||||
if power_scaling is None:
|
|
||||||
power_scaling = toml_conf.get("performance_scaling")
|
|
||||||
if power_scaling is not None:
|
|
||||||
enabled = power_scaling.get("enabled")
|
|
||||||
if not enabled:
|
|
||||||
return None
|
|
||||||
power_step = power_scaling.get("power_step")
|
|
||||||
min_power = power_scaling.get("min_psu_power_limit")
|
|
||||||
if min_power is None:
|
|
||||||
min_power = power_scaling.get("min_power_target")
|
|
||||||
sd_mode = ScalingShutdown.from_bosminer(power_scaling)
|
|
||||||
|
|
||||||
return cls(step=power_step, minimum=min_power, shutdown=sd_mode)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_boser(cls, grpc_miner_conf: dict, mode: str | None = None):
|
|
||||||
if mode == "power":
|
|
||||||
return cls._from_boser_power(grpc_miner_conf)
|
|
||||||
if mode == "hashrate":
|
|
||||||
# not implemented yet
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _from_boser_power(cls, grpc_miner_conf: dict):
|
|
||||||
try:
|
|
||||||
dps_conf = grpc_miner_conf["dps"]
|
|
||||||
if not dps_conf.get("enabled", False):
|
|
||||||
return None
|
|
||||||
except LookupError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
conf = {"shutdown": ScalingShutdown.from_boser(dps_conf)}
|
|
||||||
|
|
||||||
if dps_conf.get("minPowerTarget") is not None:
|
|
||||||
conf["minimum"] = dps_conf["minPowerTarget"]["watt"]
|
|
||||||
if dps_conf.get("powerStep") is not None:
|
|
||||||
conf["step"] = dps_conf["powerStep"]["watt"]
|
|
||||||
return cls(**conf)
|
|
||||||
@@ -17,126 +17,106 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigValue
|
from pyasic.config.base import MinerConfigValue
|
||||||
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
|
||||||
PoolConfiguration,
|
|
||||||
PoolGroupConfiguration,
|
|
||||||
Quota,
|
|
||||||
SaveAction,
|
|
||||||
SetPoolGroupsRequest,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class Pool(MinerConfigValue):
|
class Pool(MinerConfigValue):
|
||||||
url: str
|
url: str
|
||||||
user: str
|
user: str
|
||||||
password: str
|
password: str
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str | None = None) -> dict:
|
def as_am_modern(self, user_suffix: str = None) -> dict:
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
"url": self.url,
|
"url": self.url,
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
"user": f"{self.user}{user_suffix}",
|
||||||
"pass": self.password,
|
"pass": self.password,
|
||||||
}
|
}
|
||||||
|
return {"url": self.url, "user": self.user, "pass": self.password}
|
||||||
|
|
||||||
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
|
def as_wm(self, idx: int = 1, user_suffix: str = None) -> dict:
|
||||||
return {
|
if user_suffix is not None:
|
||||||
"url": self.url,
|
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
|
||||||
"pass": self.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
"url": self.url,
|
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
|
||||||
"pass": self.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
return {
|
||||||
f"pool_{idx}": self.url,
|
f"pool_{idx}": self.url,
|
||||||
f"worker_{idx}": f"{self.user}{user_suffix or ''}",
|
f"worker_{idx}": f"{self.user}{user_suffix}",
|
||||||
|
f"passwd_{idx}": self.password,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
f"pool_{idx}": self.url,
|
||||||
|
f"worker_{idx}": self.user,
|
||||||
f"passwd_{idx}": self.password,
|
f"passwd_{idx}": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_am_old(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
def as_am_old(self, idx: int = 1, user_suffix: str = None) -> dict:
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
f"_ant_pool{idx}url": self.url,
|
f"_ant_pool{idx}url": self.url,
|
||||||
f"_ant_pool{idx}user": f"{self.user}{user_suffix or ''}",
|
f"_ant_pool{idx}user": f"{self.user}{user_suffix}",
|
||||||
|
f"_ant_pool{idx}pw": self.password,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
f"_ant_pool{idx}url": self.url,
|
||||||
|
f"_ant_pool{idx}user": self.user,
|
||||||
f"_ant_pool{idx}pw": self.password,
|
f"_ant_pool{idx}pw": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str | None = None) -> dict:
|
def as_goldshell(self, user_suffix: str = None) -> dict:
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
"url": self.url,
|
"url": self.url,
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
"user": f"{self.user}{user_suffix}",
|
||||||
"pass": self.password,
|
"pass": self.password,
|
||||||
}
|
}
|
||||||
|
return {"url": self.url, "user": self.user, "pass": self.password}
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str | None = None) -> str:
|
def as_avalon(self, user_suffix: str = None) -> str:
|
||||||
return ",".join([self.url, f"{self.user}{user_suffix or ''}", self.password])
|
if user_suffix is not None:
|
||||||
|
return ",".join([self.url, f"{self.user}{user_suffix}", self.password])
|
||||||
|
return ",".join([self.url, self.user, self.password])
|
||||||
|
|
||||||
def as_inno(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
def as_inno(self, idx: int = 1, user_suffix: str = None) -> dict:
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
f"Pool{idx}": self.url,
|
f"Pool{idx}": self.url,
|
||||||
f"UserName{idx}": f"{self.user}{user_suffix or ''}",
|
f"UserName{idx}": f"{self.user}{user_suffix}",
|
||||||
|
f"Password{idx}": self.password,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
f"Pool{idx}": self.url,
|
||||||
|
f"UserName{idx}": self.user,
|
||||||
f"Password{idx}": self.password,
|
f"Password{idx}": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
def as_bosminer(self, user_suffix: str = None) -> dict:
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
"url": self.url,
|
"url": self.url,
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
"user": f"{self.user}{user_suffix}",
|
||||||
"password": self.password,
|
"password": self.password,
|
||||||
}
|
}
|
||||||
|
return {"url": self.url, "user": self.user, "password": self.password}
|
||||||
|
|
||||||
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
def as_auradine(self, user_suffix: str = None) -> dict:
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
"url": self.url,
|
"url": self.url,
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
"user": f"{self.user}{user_suffix}",
|
||||||
"pass": self.password,
|
"pass": self.password,
|
||||||
}
|
}
|
||||||
|
return {"url": self.url, "user": self.user, "pass": self.password}
|
||||||
|
|
||||||
def as_epic(self, user_suffix: str | None = None):
|
def as_epic(self, user_suffix: str = None):
|
||||||
|
if user_suffix is not None:
|
||||||
return {
|
return {
|
||||||
"pool": self.url,
|
"pool": self.url,
|
||||||
"login": f"{self.user}{user_suffix or ''}",
|
"login": f"{self.user}{user_suffix}",
|
||||||
"password": self.password,
|
"password": self.password,
|
||||||
}
|
}
|
||||||
|
return {"pool": self.url, "login": self.user, "password": self.password}
|
||||||
def as_mara(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
"url": self.url,
|
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
|
||||||
"pass": self.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
"stratumURL": self.url,
|
|
||||||
"stratumUser": f"{self.user}{user_suffix or ''}",
|
|
||||||
"stratumPassword": self.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str | None = None) -> PoolConfiguration:
|
|
||||||
return PoolConfiguration(
|
|
||||||
url=self.url,
|
|
||||||
user=f"{self.user}{user_suffix or ''}",
|
|
||||||
password=self.password,
|
|
||||||
enabled=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {
|
|
||||||
"url": self.url,
|
|
||||||
"user": f"{self.user}{user_suffix or ''}",
|
|
||||||
"pass": self.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "Pool":
|
def from_dict(cls, dict_conf: dict | None) -> "Pool":
|
||||||
@@ -160,18 +140,6 @@ class Pool(MinerConfigValue):
|
|||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hiveon_modern(cls, web_pool: dict) -> "Pool":
|
|
||||||
return cls(
|
|
||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_elphapex(cls, web_pool: dict) -> "Pool":
|
|
||||||
return cls(
|
|
||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: check if this is accurate, user/username, pass/password
|
# TODO: check if this is accurate, user/username, pass/password
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pool: dict) -> "Pool":
|
def from_goldshell(cls, web_pool: dict) -> "Pool":
|
||||||
@@ -190,13 +158,13 @@ class Pool(MinerConfigValue):
|
|||||||
return cls(
|
return cls(
|
||||||
url=toml_pool_conf["url"],
|
url=toml_pool_conf["url"],
|
||||||
user=toml_pool_conf["user"],
|
user=toml_pool_conf["user"],
|
||||||
password=toml_pool_conf.get("password", ""),
|
password=toml_pool_conf["password"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_pool: dict) -> "Pool":
|
def from_vnish(cls, web_pool: dict) -> "Pool":
|
||||||
return cls(
|
return cls(
|
||||||
url="stratum+tcp://" + web_pool["url"],
|
url=web_pool["url"],
|
||||||
user=web_pool["user"],
|
user=web_pool["user"],
|
||||||
password=web_pool["pass"],
|
password=web_pool["pass"],
|
||||||
)
|
)
|
||||||
@@ -209,40 +177,12 @@ class Pool(MinerConfigValue):
|
|||||||
password=grpc_pool["password"],
|
password=grpc_pool["password"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mara(cls, web_pool: dict) -> "Pool":
|
|
||||||
return cls(
|
|
||||||
url=web_pool["url"],
|
|
||||||
user=web_pool["user"],
|
|
||||||
password=web_pool["pass"],
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_espminer(cls, web_system_info: dict) -> "Pool":
|
|
||||||
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
|
|
||||||
return cls(
|
|
||||||
url=url,
|
|
||||||
user=web_system_info["stratumUser"],
|
|
||||||
password=web_system_info.get("stratumPassword", ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, rpc_pools: dict) -> "Pool":
|
|
||||||
return cls.from_api(rpc_pools)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_iceriver(cls, web_pool: dict) -> "Pool":
|
|
||||||
return cls(
|
|
||||||
url=web_pool["addr"],
|
|
||||||
user=web_pool["user"],
|
|
||||||
password=web_pool["pass"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class PoolGroup(MinerConfigValue):
|
class PoolGroup(MinerConfigValue):
|
||||||
pools: list[Pool] = Field(default_factory=list)
|
pools: list[Pool] = field(default_factory=list)
|
||||||
quota: int = 1
|
quota: int = 1
|
||||||
name: str | None = None
|
name: str = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
@@ -250,40 +190,18 @@ class PoolGroup(MinerConfigValue):
|
|||||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(6)
|
random.choice(string.ascii_uppercase + string.digits) for _ in range(6)
|
||||||
) # generate random pool group name in case it isn't set
|
) # generate random pool group name in case it isn't set
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str | None = None) -> list:
|
def as_am_modern(self, user_suffix: str = None) -> list:
|
||||||
pools = []
|
pools = []
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
if len(self.pools) > idx:
|
if len(self.pools) > idx:
|
||||||
pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix))
|
pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix))
|
||||||
else:
|
else:
|
||||||
pools.append(Pool(url="", user="", password="").as_am_modern())
|
pools.append(Pool("", "", "").as_am_modern())
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_hiveon_modern(self, user_suffix: str | None = None) -> list:
|
def as_wm(self, user_suffix: str = None) -> dict:
|
||||||
pools = []
|
|
||||||
idx = 0
|
|
||||||
while idx < 3:
|
|
||||||
if len(self.pools) > idx:
|
|
||||||
pools.append(self.pools[idx].as_hiveon_modern(user_suffix=user_suffix))
|
|
||||||
else:
|
|
||||||
pools.append(Pool(url="", user="", password="").as_hiveon_modern())
|
|
||||||
idx += 1
|
|
||||||
return pools
|
|
||||||
|
|
||||||
def as_elphapex(self, user_suffix: str | None = None) -> list:
|
|
||||||
pools = []
|
|
||||||
idx = 0
|
|
||||||
while idx < 3:
|
|
||||||
if len(self.pools) > idx:
|
|
||||||
pools.append(self.pools[idx].as_elphapex(user_suffix=user_suffix))
|
|
||||||
else:
|
|
||||||
pools.append(Pool(url="", user="", password="").as_elphapex())
|
|
||||||
idx += 1
|
|
||||||
return pools
|
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str | None = None) -> dict:
|
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
@@ -292,11 +210,11 @@ class PoolGroup(MinerConfigValue):
|
|||||||
**self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix)
|
**self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pools.update(**Pool(url="", user="", password="").as_wm(idx=idx + 1))
|
pools.update(**Pool("", "", "").as_wm(idx=idx + 1))
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str | None = None) -> dict:
|
def as_am_old(self, user_suffix: str = None) -> dict:
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
@@ -305,21 +223,19 @@ class PoolGroup(MinerConfigValue):
|
|||||||
**self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix)
|
**self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pools.update(
|
pools.update(**Pool("", "", "").as_am_old(idx=idx + 1))
|
||||||
**Pool(url="", user="", password="").as_am_old(idx=idx + 1)
|
|
||||||
)
|
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str | None = None) -> list:
|
def as_goldshell(self, user_suffix: str = None) -> list:
|
||||||
return [pool.as_goldshell(user_suffix) for pool in self.pools]
|
return [pool.as_goldshell(user_suffix) for pool in self.pools]
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str | None = None) -> str:
|
def as_avalon(self, user_suffix: str = None) -> str:
|
||||||
if len(self.pools) > 0:
|
if len(self.pools) > 0:
|
||||||
return self.pools[0].as_avalon(user_suffix=user_suffix)
|
return self.pools[0].as_avalon(user_suffix=user_suffix)
|
||||||
return Pool(url="", user="", password="").as_avalon()
|
return Pool("", "", "").as_avalon()
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str | None = None) -> dict:
|
def as_inno(self, user_suffix: str = None) -> dict:
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
@@ -328,11 +244,11 @@ class PoolGroup(MinerConfigValue):
|
|||||||
**self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix)
|
**self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pools.update(**Pool(url="", user="", password="").as_inno(idx=idx + 1))
|
pools.update(**Pool("", "", "").as_inno(idx=idx + 1))
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
def as_bosminer(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.pools) > 0:
|
if len(self.pools) > 0:
|
||||||
conf = {
|
conf = {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
@@ -345,28 +261,12 @@ class PoolGroup(MinerConfigValue):
|
|||||||
return conf
|
return conf
|
||||||
return {"name": "Group", "pool": []}
|
return {"name": "Group", "pool": []}
|
||||||
|
|
||||||
def as_auradine(self, user_suffix: str | None = None) -> list:
|
def as_auradine(self, user_suffix: str = None) -> list:
|
||||||
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
|
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
|
||||||
|
|
||||||
def as_epic(self, user_suffix: str | None = None) -> list:
|
def as_epic(self, user_suffix: str = None) -> dict:
|
||||||
return [p.as_epic(user_suffix=user_suffix) for p in self.pools]
|
return [p.as_epic(user_suffix=user_suffix) for p in self.pools]
|
||||||
|
|
||||||
def as_mara(self, user_suffix: str | None = None) -> list:
|
|
||||||
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
|
|
||||||
|
|
||||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return self.pools[0].as_espminer(user_suffix=user_suffix)
|
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
|
|
||||||
return PoolGroupConfiguration(
|
|
||||||
name=self.name,
|
|
||||||
quota=Quota(value=self.quota),
|
|
||||||
pools=[p.as_boser() for p in self.pools],
|
|
||||||
)
|
|
||||||
|
|
||||||
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {"pools": [p.as_vnish(user_suffix=user_suffix) for p in self.pools]}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
|
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
@@ -399,43 +299,27 @@ class PoolGroup(MinerConfigValue):
|
|||||||
pools.append(Pool.from_am_modern(pool))
|
pools.append(Pool.from_am_modern(pool))
|
||||||
return cls(pools=pools)
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hiveon_modern(cls, web_pool_list: list) -> "PoolGroup":
|
|
||||||
pools = []
|
|
||||||
for pool in web_pool_list:
|
|
||||||
pools.append(Pool.from_hiveon_modern(pool))
|
|
||||||
return cls(pools=pools)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_elphapex(cls, web_pool_list: list) -> "PoolGroup":
|
|
||||||
pools = []
|
|
||||||
for pool in web_pool_list:
|
|
||||||
pools.append(Pool.from_elphapex(pool))
|
|
||||||
return cls(pools=pools)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
||||||
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
|
return cls([Pool.from_goldshell(p) for p in web_pools])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_inno(cls, web_pools: list) -> "PoolGroup":
|
def from_inno(cls, web_pools: list) -> "PoolGroup":
|
||||||
return cls(pools=[Pool.from_inno(p) for p in web_pools])
|
return cls([Pool.from_inno(p) for p in web_pools])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup":
|
def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup":
|
||||||
if toml_group_conf.get("pool") is not None:
|
if toml_group_conf.get("pool") is not None:
|
||||||
return cls(
|
return cls(
|
||||||
name=toml_group_conf["name"],
|
name=toml_group_conf["name"],
|
||||||
quota=toml_group_conf.get("quota", 1),
|
quota=toml_group_conf.get("quota"),
|
||||||
pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]],
|
pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]],
|
||||||
)
|
)
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
|
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
|
||||||
return cls(
|
return cls([Pool.from_vnish(p) for p in web_settings_pools])
|
||||||
pools=[Pool.from_vnish(p) for p in web_settings_pools if p["url"] != ""]
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup":
|
def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup":
|
||||||
@@ -452,26 +336,10 @@ class PoolGroup(MinerConfigValue):
|
|||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mara(cls, web_config_pools: dict) -> "PoolGroup":
|
|
||||||
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_espminer(cls, web_system_info: dict) -> "PoolGroup":
|
|
||||||
return cls(pools=[Pool.from_espminer(web_system_info)])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup":
|
|
||||||
return cls(
|
|
||||||
pools=[
|
|
||||||
Pool.from_iceriver(web_pool)
|
|
||||||
for web_pool in web_userpanel["data"]["pools"]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class PoolConfig(MinerConfigValue):
|
class PoolConfig(MinerConfigValue):
|
||||||
groups: List[PoolGroup] = Field(default_factory=list)
|
groups: List[PoolGroup] = field(default_factory=list)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls) -> "PoolConfig":
|
def default(cls) -> "PoolConfig":
|
||||||
@@ -493,62 +361,47 @@ class PoolConfig(MinerConfigValue):
|
|||||||
group_pools.append(pool)
|
group_pools.append(pool)
|
||||||
return cls(groups=[PoolGroup(pools=group_pools)])
|
return cls(groups=[PoolGroup(pools=group_pools)])
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str | None = None) -> dict:
|
def as_am_modern(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_am_modern()}
|
return {"pools": PoolGroup().as_am_modern()}
|
||||||
|
|
||||||
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
|
def as_wm(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
|
||||||
return {"pools": self.groups[0].as_hiveon_modern(user_suffix=user_suffix)}
|
|
||||||
return {"pools": PoolGroup().as_hiveon_modern()}
|
|
||||||
|
|
||||||
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
|
||||||
if len(self.groups) > 0:
|
|
||||||
return {"pools": self.groups[0].as_elphapex(user_suffix=user_suffix)}
|
|
||||||
return {"pools": PoolGroup().as_elphapex()}
|
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str | None = None) -> dict:
|
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_wm()}
|
return {"pools": PoolGroup().as_wm()}
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str | None = None) -> dict:
|
def as_am_old(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return self.groups[0].as_am_old(user_suffix=user_suffix)
|
return self.groups[0].as_am_old(user_suffix=user_suffix)
|
||||||
return PoolGroup().as_am_old()
|
return PoolGroup().as_am_old()
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str | None = None) -> dict:
|
def as_goldshell(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_goldshell(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_goldshell(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_goldshell()}
|
return {"pools": PoolGroup().as_goldshell()}
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str | None = None) -> dict:
|
def as_avalon(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_avalon()}
|
return {"pools": PoolGroup().as_avalon()}
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str | None = None) -> dict:
|
def as_inno(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return self.groups[0].as_inno(user_suffix=user_suffix)
|
return self.groups[0].as_inno(user_suffix=user_suffix)
|
||||||
return PoolGroup().as_inno()
|
return PoolGroup().as_inno()
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
def as_bosminer(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {
|
return {
|
||||||
"group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups]
|
"group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups]
|
||||||
}
|
}
|
||||||
return {"group": [PoolGroup().as_bosminer()]}
|
return {"group": [PoolGroup().as_bosminer()]}
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str | None = None) -> dict:
|
def as_boser(self, user_suffix: str = None) -> dict:
|
||||||
return {
|
return {}
|
||||||
"set_pool_groups": SetPoolGroupsRequest(
|
|
||||||
save_action=SaveAction.SAVE_AND_APPLY,
|
|
||||||
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
def as_auradine(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {
|
return {
|
||||||
"updatepools": {
|
"updatepools": {
|
||||||
@@ -557,7 +410,7 @@ class PoolConfig(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
return {"updatepools": {"pools": PoolGroup().as_auradine()}}
|
return {"updatepools": {"pools": PoolGroup().as_auradine()}}
|
||||||
|
|
||||||
def as_epic(self, user_suffix: str | None = None) -> dict:
|
def as_epic(self, user_suffix: str = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {
|
return {
|
||||||
"pools": {
|
"pools": {
|
||||||
@@ -574,20 +427,6 @@ class PoolConfig(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_mara(self, user_suffix: str | None = None) -> dict:
|
|
||||||
if len(self.groups) > 0:
|
|
||||||
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
|
|
||||||
return {"pools": []}
|
|
||||||
|
|
||||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return self.groups[0].as_espminer(user_suffix=user_suffix)
|
|
||||||
|
|
||||||
def as_luxos(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
|
||||||
return self.groups[0].as_vnish(user_suffix=user_suffix)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api(cls, api_pools: dict) -> "PoolConfig":
|
def from_api(cls, api_pools: dict) -> "PoolConfig":
|
||||||
try:
|
try:
|
||||||
@@ -596,56 +435,38 @@ class PoolConfig(MinerConfigValue):
|
|||||||
return PoolConfig.default()
|
return PoolConfig.default()
|
||||||
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
|
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
|
||||||
|
|
||||||
return cls(groups=[PoolGroup.from_api(pool_data)])
|
return cls([PoolGroup.from_api(pool_data)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict) -> "PoolConfig":
|
def from_epic(cls, web_conf: dict) -> "PoolConfig":
|
||||||
pool_data = web_conf["StratumConfigs"]
|
pool_data = web_conf["StratumConfigs"]
|
||||||
return cls(groups=[PoolGroup.from_epic(pool_data)])
|
return cls([PoolGroup.from_epic(pool_data)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
|
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
|
||||||
try:
|
|
||||||
pool_data = web_conf["pools"]
|
|
||||||
except KeyError:
|
|
||||||
return cls(groups=[])
|
|
||||||
|
|
||||||
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_hiveon_modern(cls, web_conf: dict) -> "PoolConfig":
|
|
||||||
try:
|
|
||||||
pool_data = web_conf["pools"]
|
|
||||||
except KeyError:
|
|
||||||
return cls(groups=[])
|
|
||||||
|
|
||||||
return cls(groups=[PoolGroup.from_hiveon_modern(pool_data)])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_elphapex(cls, web_conf: dict) -> "PoolConfig":
|
|
||||||
pool_data = web_conf["pools"]
|
pool_data = web_conf["pools"]
|
||||||
|
|
||||||
return cls(groups=[PoolGroup.from_elphapex(pool_data)])
|
return cls([PoolGroup.from_am_modern(pool_data)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
|
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
|
||||||
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
|
return cls([PoolGroup.from_goldshell(web_pools)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_inno(cls, web_pools: list) -> "PoolConfig":
|
def from_inno(cls, web_pools: list) -> "PoolConfig":
|
||||||
return cls(groups=[PoolGroup.from_inno(web_pools)])
|
return cls([PoolGroup.from_inno(web_pools)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_conf: dict) -> "PoolConfig":
|
def from_bosminer(cls, toml_conf: dict) -> "PoolConfig":
|
||||||
if toml_conf.get("group") is None:
|
if toml_conf.get("group") is None:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
return cls(groups=[PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
|
return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict) -> "PoolConfig":
|
def from_vnish(cls, web_settings: dict) -> "PoolConfig":
|
||||||
try:
|
try:
|
||||||
return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])])
|
return cls([PoolGroup.from_vnish(web_settings["miner"]["pools"])])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@@ -660,32 +481,3 @@ class PoolConfig(MinerConfigValue):
|
|||||||
)
|
)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mara(cls, web_config: dict) -> "PoolConfig":
|
|
||||||
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_espminer(cls, web_system_info: dict) -> "PoolConfig":
|
|
||||||
return cls(groups=[PoolGroup.from_espminer(web_system_info)])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":
|
|
||||||
return cls(groups=[PoolGroup.from_iceriver(web_userpanel)])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> "PoolConfig":
|
|
||||||
return cls(
|
|
||||||
groups=[
|
|
||||||
PoolGroup(
|
|
||||||
pools=[
|
|
||||||
Pool.from_luxos(pool)
|
|
||||||
for pool in rpc_pools["POOLS"]
|
|
||||||
if pool["GROUP"] == group["GROUP"]
|
|
||||||
],
|
|
||||||
name=group["Name"],
|
|
||||||
quota=group["Quota"],
|
|
||||||
)
|
|
||||||
for group in rpc_groups["GROUPS"]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|||||||
221
pyasic/config/power_scaling.py
Normal file
221
pyasic/config/power_scaling.py
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
||||||
|
DpsPowerTarget,
|
||||||
|
DpsTarget,
|
||||||
|
Power,
|
||||||
|
SetDpsRequest,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PowerScalingShutdownEnabled(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="enabled")
|
||||||
|
duration: int = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingShutdownEnabled":
|
||||||
|
return cls(duration=dict_conf.get("duration"))
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
cfg = {"shutdown_enabled": True}
|
||||||
|
|
||||||
|
if self.duration is not None:
|
||||||
|
cfg["shutdown_duration"] = self.duration
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
return {"enable_shutdown": True, "shutdown_duration": self.duration}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PowerScalingShutdownDisabled(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="disabled")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingShutdownDisabled":
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
return {"shutdown_enabled": False}
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
return {"enable_shutdown ": False}
|
||||||
|
|
||||||
|
|
||||||
|
class PowerScalingShutdown(MinerConfigOption):
|
||||||
|
enabled = PowerScalingShutdownEnabled
|
||||||
|
disabled = PowerScalingShutdownDisabled
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
mode = dict_conf.get("mode")
|
||||||
|
if mode is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
clsattr = getattr(cls, mode)
|
||||||
|
if clsattr is not None:
|
||||||
|
return clsattr().from_dict(dict_conf)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, power_scaling_conf: dict):
|
||||||
|
sd_enabled = power_scaling_conf.get("shutdown_enabled")
|
||||||
|
if sd_enabled is not None:
|
||||||
|
if sd_enabled:
|
||||||
|
return cls.enabled(power_scaling_conf.get("shutdown_duration"))
|
||||||
|
else:
|
||||||
|
return cls.disabled()
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, power_scaling_conf: dict):
|
||||||
|
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
||||||
|
if sd_enabled is not None:
|
||||||
|
if sd_enabled:
|
||||||
|
try:
|
||||||
|
return cls.enabled(power_scaling_conf["shutdownDuration"]["hours"])
|
||||||
|
except KeyError:
|
||||||
|
return cls.enabled()
|
||||||
|
else:
|
||||||
|
return cls.disabled()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PowerScalingEnabled(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="enabled")
|
||||||
|
power_step: int = None
|
||||||
|
minimum_power: int = None
|
||||||
|
shutdown_enabled: PowerScalingShutdownEnabled | PowerScalingShutdownDisabled = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, power_scaling_conf: dict) -> "PowerScalingEnabled":
|
||||||
|
power_step = power_scaling_conf.get("power_step")
|
||||||
|
min_power = power_scaling_conf.get("min_psu_power_limit")
|
||||||
|
if min_power is None:
|
||||||
|
min_power = power_scaling_conf.get("min_power_target")
|
||||||
|
sd_mode = PowerScalingShutdown.from_bosminer(power_scaling_conf)
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
power_step=power_step, minimum_power=min_power, shutdown_enabled=sd_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingEnabled":
|
||||||
|
cls_conf = {
|
||||||
|
"power_step": dict_conf.get("power_step"),
|
||||||
|
"minimum_power": dict_conf.get("minimum_power"),
|
||||||
|
}
|
||||||
|
shutdown_enabled = dict_conf.get("shutdown_enabled")
|
||||||
|
if shutdown_enabled is not None:
|
||||||
|
cls_conf["shutdown_enabled"] = PowerScalingShutdown.from_dict(
|
||||||
|
shutdown_enabled
|
||||||
|
)
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
cfg = {"enabled": True}
|
||||||
|
if self.power_step is not None:
|
||||||
|
cfg["power_step"] = self.power_step
|
||||||
|
if self.minimum_power is not None:
|
||||||
|
cfg["min_power_target"] = self.minimum_power
|
||||||
|
|
||||||
|
if self.shutdown_enabled is not None:
|
||||||
|
cfg = {**cfg, **self.shutdown_enabled.as_bosminer()}
|
||||||
|
|
||||||
|
return {"performance_scaling": cfg}
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
return {
|
||||||
|
"set_dps": SetDpsRequest(
|
||||||
|
enable=True,
|
||||||
|
**self.shutdown_enabled.as_boser(),
|
||||||
|
target=DpsTarget(
|
||||||
|
power_target=DpsPowerTarget(
|
||||||
|
power_step=Power(self.power_step),
|
||||||
|
min_power_target=Power(self.minimum_power),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PowerScalingDisabled(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="disabled")
|
||||||
|
|
||||||
|
|
||||||
|
class PowerScalingConfig(MinerConfigOption):
|
||||||
|
enabled = PowerScalingEnabled
|
||||||
|
disabled = PowerScalingDisabled
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls):
|
||||||
|
return cls.disabled()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
mode = dict_conf.get("mode")
|
||||||
|
if mode is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
clsattr = getattr(cls, mode)
|
||||||
|
if clsattr is not None:
|
||||||
|
return clsattr().from_dict(dict_conf)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_conf: dict):
|
||||||
|
power_scaling = toml_conf.get("power_scaling")
|
||||||
|
if power_scaling is None:
|
||||||
|
power_scaling = toml_conf.get("performance_scaling")
|
||||||
|
if power_scaling is not None:
|
||||||
|
enabled = power_scaling.get("enabled")
|
||||||
|
if enabled is not None:
|
||||||
|
if enabled:
|
||||||
|
return cls.enabled().from_bosminer(power_scaling)
|
||||||
|
else:
|
||||||
|
return cls.disabled()
|
||||||
|
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, grpc_miner_conf: dict):
|
||||||
|
try:
|
||||||
|
dps_conf = grpc_miner_conf["dps"]
|
||||||
|
if not dps_conf.get("enabled", False):
|
||||||
|
return cls.disabled()
|
||||||
|
except LookupError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
conf = {"shutdown_enabled": PowerScalingShutdown.from_boser(dps_conf)}
|
||||||
|
|
||||||
|
if dps_conf.get("minPowerTarget") is not None:
|
||||||
|
conf["minimum_power"] = dps_conf["minPowerTarget"]["watt"]
|
||||||
|
if dps_conf.get("powerStep") is not None:
|
||||||
|
conf["power_step"] = dps_conf["powerStep"]["watt"]
|
||||||
|
return cls.enabled(**conf)
|
||||||
@@ -15,13 +15,16 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigValue
|
from pyasic.config.base import MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class TemperatureConfig(MinerConfigValue):
|
class TemperatureConfig(MinerConfigValue):
|
||||||
target: int | None = None
|
target: int = None
|
||||||
hot: int | None = None
|
hot: int = None
|
||||||
danger: int | None = None
|
danger: int = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls):
|
def default(cls):
|
||||||
@@ -35,8 +38,6 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
temp_cfg["hot_temp"] = self.hot
|
temp_cfg["hot_temp"] = self.hot
|
||||||
if self.danger is not None:
|
if self.danger is not None:
|
||||||
temp_cfg["dangerous_temp"] = self.danger
|
temp_cfg["dangerous_temp"] = self.danger
|
||||||
if len(temp_cfg) == 0:
|
|
||||||
return {}
|
|
||||||
return {"temp_control": temp_cfg}
|
return {"temp_control": temp_cfg}
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
def as_epic(self) -> dict:
|
||||||
@@ -46,17 +47,9 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
else:
|
else:
|
||||||
temps_config["fans"]["Auto"]["Target Temperature"] = 60
|
temps_config["fans"]["Auto"]["Target Temperature"] = 60
|
||||||
if self.danger is not None:
|
if self.danger is not None:
|
||||||
temps_config["temps"]["critical"] = self.danger
|
temps_config["temps"]["shutdown"] = self.danger
|
||||||
if self.hot is not None:
|
|
||||||
temps_config["temps"]["shutdown"] = self.hot
|
|
||||||
return temps_config
|
return temps_config
|
||||||
|
|
||||||
def as_luxos(self) -> dict:
|
|
||||||
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
|
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
|
||||||
return {"misc": {"restart_temp": self.danger}}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
|
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
|
||||||
return cls(
|
return cls(
|
||||||
@@ -74,38 +67,26 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
hot=temp_control.get("hot_temp"),
|
hot=temp_control.get("hot_temp"),
|
||||||
danger=temp_control.get("dangerous_temp"),
|
danger=temp_control.get("dangerous_temp"),
|
||||||
)
|
)
|
||||||
return cls()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
|
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
|
||||||
try:
|
try:
|
||||||
dangerous_temp = web_conf["Misc"]["Critical Temp"]
|
dangerous_temp = web_conf["Misc"]["Shutdown Temp"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
dangerous_temp = None
|
dangerous_temp = None
|
||||||
try:
|
|
||||||
hot_temp = web_conf["Misc"]["Shutdown Temp"]
|
|
||||||
except KeyError:
|
|
||||||
hot_temp = None
|
|
||||||
# Need to do this in two blocks to avoid KeyError if one is missing
|
# Need to do this in two blocks to avoid KeyError if one is missing
|
||||||
try:
|
try:
|
||||||
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
|
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
target_temp = None
|
target_temp = None
|
||||||
|
|
||||||
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp)
|
return cls(target=target_temp, danger=dangerous_temp)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":
|
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":
|
||||||
try:
|
|
||||||
dangerous_temp = web_settings["misc"]["restart_temp"]
|
|
||||||
except KeyError:
|
|
||||||
dangerous_temp = None
|
|
||||||
try:
|
try:
|
||||||
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
|
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
|
||||||
return cls(
|
return cls(target=web_settings["miner"]["cooling"]["mode"]["param"])
|
||||||
target=web_settings["miner"]["cooling"]["mode"]["param"],
|
|
||||||
danger=dangerous_temp,
|
|
||||||
)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return cls()
|
return cls()
|
||||||
@@ -140,16 +121,3 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
|
|
||||||
return cls(**conf)
|
return cls(**conf)
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_luxos(cls, rpc_tempctrl: dict) -> "TemperatureConfig":
|
|
||||||
try:
|
|
||||||
tempctrl_config = rpc_tempctrl["TEMPCTRL"][0]
|
|
||||||
return cls(
|
|
||||||
target=tempctrl_config.get("Target"),
|
|
||||||
hot=tempctrl_config.get("Hot"),
|
|
||||||
danger=tempctrl_config.get("Dangerous"),
|
|
||||||
)
|
|
||||||
except LookupError:
|
|
||||||
pass
|
|
||||||
return cls.default()
|
|
||||||
|
|||||||
@@ -13,26 +13,24 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import copy
|
|
||||||
import time
|
|
||||||
from datetime import datetime, timezone
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, computed_field
|
import copy
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from dataclasses import asdict, dataclass, field, fields
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any, List, Union
|
||||||
|
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.config.mining import MiningModePowerTune
|
from pyasic.config.mining import MiningModePowerTune
|
||||||
from pyasic.data.pools import PoolMetrics, Scheme
|
|
||||||
from pyasic.device.algorithm.hashrate import AlgoHashRateType
|
|
||||||
|
|
||||||
from .boards import HashBoard
|
from .boards import HashBoard
|
||||||
from .device import DeviceInfo
|
|
||||||
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
||||||
from .error_codes.base import BaseMinerError
|
|
||||||
from .fans import Fan
|
from .fans import Fan
|
||||||
|
|
||||||
|
|
||||||
class MinerData(BaseModel):
|
@dataclass
|
||||||
|
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()`)
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -40,25 +38,20 @@ class MinerData(BaseModel):
|
|||||||
datetime: The time and date this data was generated.
|
datetime: The time and date this data was generated.
|
||||||
uptime: The uptime of the miner in seconds.
|
uptime: The uptime of the miner in seconds.
|
||||||
mac: The MAC address of the miner as a str.
|
mac: The MAC address of the miner as a str.
|
||||||
device_info: Info about the device, such as model, make, and firmware.
|
|
||||||
model: The model of the miner as a str.
|
model: The model of the miner as a str.
|
||||||
make: The make of the miner as a str.
|
make: The make of the miner as a str.
|
||||||
firmware: The firmware on the miner as a str.
|
|
||||||
algo: The mining algorithm of the miner as a str.
|
|
||||||
api_ver: The current api version on the miner as a str.
|
api_ver: The current api version on the miner as a str.
|
||||||
fw_ver: The current firmware version on the miner as a str.
|
fw_ver: The current firmware version on the miner as a str.
|
||||||
hostname: The network hostname of the miner as a str.
|
hostname: The network hostname of the miner as a str.
|
||||||
hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically.
|
hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically.
|
||||||
|
_hashrate: Backup for hashrate found via API instead of hashboards.
|
||||||
expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
|
expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
|
||||||
sticker_hashrate: The factory sticker hashrate of the miner as a float.
|
|
||||||
hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics.
|
hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics.
|
||||||
temperature_avg: The average temperature across the boards. Calculated automatically.
|
temperature_avg: The average temperature across the boards. Calculated automatically.
|
||||||
env_temp: The environment temps as a float.
|
env_temp: The environment temps as a float.
|
||||||
wattage: Current power draw of the miner as an int.
|
wattage: Current power draw of the miner as an int.
|
||||||
voltage: Current output voltage of the PSU as an float.
|
|
||||||
wattage_limit: Power limit of the miner as an int.
|
wattage_limit: Power limit of the miner as an int.
|
||||||
fans: A list of fans on the miner with their speeds.
|
fans: A list of fans on the miner with their speeds.
|
||||||
expected_fans: The number of fans expected on a miner.
|
|
||||||
fan_psu: The speed of the PSU on the fan if the miner collects it.
|
fan_psu: The speed of the PSU on the fan if the miner collects it.
|
||||||
total_chips: The total number of chips on all boards. Calculated automatically.
|
total_chips: The total number of chips on all boards. Calculated automatically.
|
||||||
expected_chips: The expected number of chips in the miner as an int.
|
expected_chips: The expected number of chips in the miner as an int.
|
||||||
@@ -71,68 +64,58 @@ class MinerData(BaseModel):
|
|||||||
fault_light: Whether the fault light is on as a boolean.
|
fault_light: Whether the fault light is on as a boolean.
|
||||||
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
|
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
|
||||||
is_mining: Whether the miner is mining.
|
is_mining: Whether the miner is mining.
|
||||||
pools: A list of PoolMetrics instances, each representing metrics for a pool.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# general
|
|
||||||
ip: str
|
ip: str
|
||||||
raw_datetime: datetime = Field(
|
datetime: datetime = None
|
||||||
exclude=True, default_factory=datetime.now(timezone.utc).astimezone, repr=False
|
uptime: int = None
|
||||||
)
|
mac: str = None
|
||||||
|
model: str = None
|
||||||
# about
|
make: str = None
|
||||||
device_info: DeviceInfo | None = None
|
api_ver: str = None
|
||||||
mac: str | None = None
|
fw_ver: str = None
|
||||||
api_ver: str | None = None
|
hostname: str = None
|
||||||
fw_ver: str | None = None
|
hashrate: float = field(init=False)
|
||||||
hostname: str | None = None
|
_hashrate: float = field(repr=False, default=None)
|
||||||
|
expected_hashrate: float = None
|
||||||
# hashrate
|
hashboards: List[HashBoard] = field(default_factory=list)
|
||||||
raw_hashrate: AlgoHashRateType = Field(exclude=True, default=None, repr=False)
|
expected_hashboards: int = None
|
||||||
|
temperature_avg: int = field(init=False)
|
||||||
# sticker
|
env_temp: float = None
|
||||||
sticker_hashrate: AlgoHashRateType | None = None
|
wattage: int = None
|
||||||
|
wattage_limit: int = field(init=False)
|
||||||
# expected
|
_wattage_limit: int = field(repr=False, default=None)
|
||||||
expected_hashrate: AlgoHashRateType | None = None
|
fans: List[Fan] = field(default_factory=list)
|
||||||
expected_hashboards: int | None = None
|
fan_psu: int = None
|
||||||
expected_chips: int | None = None
|
total_chips: int = field(init=False)
|
||||||
expected_fans: int | None = None
|
expected_chips: int = None
|
||||||
|
percent_expected_chips: float = field(init=False)
|
||||||
# temperature
|
percent_expected_hashrate: float = field(init=False)
|
||||||
env_temp: float | None = None
|
percent_expected_wattage: float = field(init=False)
|
||||||
|
nominal: bool = field(init=False)
|
||||||
# power
|
config: MinerConfig = None
|
||||||
wattage: int | None = None
|
errors: List[
|
||||||
voltage: float | None = None
|
Union[
|
||||||
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False)
|
WhatsminerError,
|
||||||
|
BraiinsOSError,
|
||||||
# fans
|
X19Error,
|
||||||
fans: list[Fan] = Field(default_factory=list)
|
InnosiliconError,
|
||||||
fan_psu: int | None = None
|
]
|
||||||
|
] = field(default_factory=list)
|
||||||
# boards
|
fault_light: Union[bool, None] = None
|
||||||
hashboards: list[HashBoard] = Field(default_factory=list)
|
efficiency: int = field(init=False)
|
||||||
|
|
||||||
# config
|
|
||||||
config: MinerConfig | None = None
|
|
||||||
fault_light: bool | None = None
|
|
||||||
|
|
||||||
# errors
|
|
||||||
errors: list[BaseMinerError] = Field(default_factory=list)
|
|
||||||
|
|
||||||
# mining state
|
|
||||||
is_mining: bool = True
|
is_mining: bool = True
|
||||||
uptime: int | None = None
|
|
||||||
|
|
||||||
# pools
|
|
||||||
pools: list[PoolMetrics] = Field(default_factory=list)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls) -> set:
|
def fields(cls):
|
||||||
all_fields = set(cls.model_fields.keys())
|
return [f.name for f in fields(cls) if not f.name.startswith("_")]
|
||||||
all_fields.update(set(cls.model_computed_fields.keys()))
|
|
||||||
return all_fields
|
@staticmethod
|
||||||
|
def dict_factory(x):
|
||||||
|
return {k: v for (k, v) in x if not k.startswith("_")}
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.datetime = datetime.now(timezone.utc).astimezone()
|
||||||
|
|
||||||
def get(self, __key: str, default: Any = None):
|
def get(self, __key: str, default: Any = None):
|
||||||
try:
|
try:
|
||||||
@@ -160,19 +143,19 @@ class MinerData(BaseModel):
|
|||||||
|
|
||||||
def __floordiv__(self, other):
|
def __floordiv__(self, other):
|
||||||
cp = copy.deepcopy(self)
|
cp = copy.deepcopy(self)
|
||||||
for key in self.fields():
|
for key in self:
|
||||||
item = getattr(self, key)
|
item = getattr(self, key)
|
||||||
if isinstance(item, int):
|
if isinstance(item, int):
|
||||||
setattr(cp, key, item // other)
|
setattr(cp, key, item // other)
|
||||||
if isinstance(item, float):
|
if isinstance(item, float):
|
||||||
setattr(cp, key, item / other)
|
setattr(cp, key, round(item / other, 2))
|
||||||
return cp
|
return cp
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if not isinstance(other, MinerData):
|
if not isinstance(other, MinerData):
|
||||||
raise TypeError("Cannot add MinerData to non MinerData type.")
|
raise TypeError("Cannot add MinerData to non MinerData type.")
|
||||||
cp = copy.deepcopy(self)
|
cp = copy.deepcopy(self)
|
||||||
for key in self.fields():
|
for key in self:
|
||||||
item = getattr(self, key)
|
item = getattr(self, key)
|
||||||
other_item = getattr(other, key)
|
other_item = getattr(other, key)
|
||||||
if item is None:
|
if item is None:
|
||||||
@@ -192,37 +175,34 @@ class MinerData(BaseModel):
|
|||||||
setattr(cp, key, item & other_item)
|
setattr(cp, key, item & other_item)
|
||||||
return cp
|
return cp
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def hashrate(self) -> AlgoHashRateType | None:
|
def hashrate(self): # noqa - Skip PyCharm inspection
|
||||||
if len(self.hashboards) > 0:
|
if len(self.hashboards) > 0:
|
||||||
hr_data = []
|
hr_data = []
|
||||||
for item in self.hashboards:
|
for item in self.hashboards:
|
||||||
if item.hashrate is not None:
|
if item.hashrate is not None:
|
||||||
hr_data.append(item.hashrate)
|
hr_data.append(item.hashrate)
|
||||||
if len(hr_data) > 0:
|
if len(hr_data) > 0:
|
||||||
return sum(hr_data, start=self.device_info.algo.hashrate(rate=0))
|
return round(sum(hr_data), 2)
|
||||||
return self.raw_hashrate
|
return self._hashrate
|
||||||
|
|
||||||
@hashrate.setter
|
@hashrate.setter
|
||||||
def hashrate(self, val):
|
def hashrate(self, val):
|
||||||
self.raw_hashrate = val
|
self._hashrate = val
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def wattage_limit(self) -> int | None:
|
def wattage_limit(self): # noqa - Skip PyCharm inspection
|
||||||
if self.config is not None:
|
if self.config is not None:
|
||||||
if isinstance(self.config.mining_mode, MiningModePowerTune):
|
if isinstance(self.config.mining_mode, MiningModePowerTune):
|
||||||
return self.config.mining_mode.power
|
return self.config.mining_mode.power
|
||||||
return self.raw_wattage_limit
|
return self._wattage_limit
|
||||||
|
|
||||||
@wattage_limit.setter
|
@wattage_limit.setter
|
||||||
def wattage_limit(self, val: int):
|
def wattage_limit(self, val: int):
|
||||||
self.raw_wattage_limit = val
|
self._wattage_limit = val
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def total_chips(self) -> int | None:
|
def total_chips(self): # noqa - Skip PyCharm inspection
|
||||||
if len(self.hashboards) > 0:
|
if len(self.hashboards) > 0:
|
||||||
chip_data = []
|
chip_data = []
|
||||||
for item in self.hashboards:
|
for item in self.hashboards:
|
||||||
@@ -232,45 +212,58 @@ class MinerData(BaseModel):
|
|||||||
return sum(chip_data)
|
return sum(chip_data)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
@total_chips.setter
|
||||||
|
def total_chips(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nominal(self) -> bool | None:
|
def nominal(self): # noqa - Skip PyCharm inspection
|
||||||
if self.total_chips is None or self.expected_chips is None:
|
if self.total_chips is None or self.expected_chips is None:
|
||||||
return None
|
return None
|
||||||
return self.expected_chips == self.total_chips
|
return self.expected_chips == self.total_chips
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
@nominal.setter
|
||||||
|
def nominal(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percent_expected_chips(self) -> int | None:
|
def percent_expected_chips(self): # noqa - Skip PyCharm inspection
|
||||||
if self.total_chips is None or self.expected_chips is None:
|
if self.total_chips is None or self.expected_chips is None:
|
||||||
return None
|
return None
|
||||||
if self.total_chips == 0 or self.expected_chips == 0:
|
if self.total_chips == 0 or self.expected_chips == 0:
|
||||||
return 0
|
return 0
|
||||||
return round((self.total_chips / self.expected_chips) * 100)
|
return round((self.total_chips / self.expected_chips) * 100)
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
@percent_expected_chips.setter
|
||||||
|
def percent_expected_chips(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percent_expected_hashrate(self) -> int | None:
|
def percent_expected_hashrate(self): # noqa - Skip PyCharm inspection
|
||||||
if self.hashrate is None or self.expected_hashrate is None:
|
if self.hashrate is None or self.expected_hashrate is None:
|
||||||
return None
|
return None
|
||||||
try:
|
if self.hashrate == 0 or self.expected_hashrate == 0:
|
||||||
return round((self.hashrate / self.expected_hashrate) * 100)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
return 0
|
return 0
|
||||||
|
return round((self.hashrate / self.expected_hashrate) * 100)
|
||||||
|
|
||||||
|
@percent_expected_hashrate.setter
|
||||||
|
def percent_expected_hashrate(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def percent_expected_wattage(self) -> int | None:
|
def percent_expected_wattage(self): # noqa - Skip PyCharm inspection
|
||||||
if self.wattage_limit is None or self.wattage is None:
|
if self.wattage_limit is None or self.wattage is None:
|
||||||
return None
|
return None
|
||||||
try:
|
if self.wattage_limit == 0 or self.wattage == 0:
|
||||||
return round((self.wattage / self.wattage_limit) * 100)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
return 0
|
return 0
|
||||||
|
return round((self.wattage / self.wattage_limit) * 100)
|
||||||
|
|
||||||
|
@percent_expected_wattage.setter
|
||||||
|
def percent_expected_wattage(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def temperature_avg(self) -> int | None:
|
def temperature_avg(self): # noqa - Skip PyCharm inspection
|
||||||
total_temp = 0
|
total_temp = 0
|
||||||
temp_count = 0
|
temp_count = 0
|
||||||
for hb in self.hashboards:
|
for hb in self.hashboards:
|
||||||
@@ -281,55 +274,24 @@ class MinerData(BaseModel):
|
|||||||
return None
|
return None
|
||||||
return round(total_temp / temp_count)
|
return round(total_temp / temp_count)
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
@temperature_avg.setter
|
||||||
|
def temperature_avg(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def efficiency(self) -> int | None:
|
def efficiency(self): # noqa - Skip PyCharm inspection
|
||||||
if self.hashrate is None or self.wattage is None:
|
if self.hashrate is None or self.wattage is None:
|
||||||
return None
|
return None
|
||||||
try:
|
if self.hashrate == 0 or self.wattage == 0:
|
||||||
return round(self.wattage / float(self.hashrate))
|
|
||||||
except ZeroDivisionError:
|
|
||||||
return 0
|
return 0
|
||||||
|
return round(self.wattage / self.hashrate)
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
@efficiency.setter
|
||||||
@property
|
def efficiency(self, val):
|
||||||
def datetime(self) -> str:
|
pass
|
||||||
return self.raw_datetime.isoformat()
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def timestamp(self) -> int:
|
|
||||||
return int(time.mktime(self.raw_datetime.timetuple()))
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def make(self) -> str | None:
|
|
||||||
if self.device_info.make is not None:
|
|
||||||
return str(self.device_info.make)
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def model(self) -> str | None:
|
|
||||||
if self.device_info.model is not None:
|
|
||||||
return str(self.device_info.model)
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def firmware(self) -> str | None:
|
|
||||||
if self.device_info.firmware is not None:
|
|
||||||
return str(self.device_info.firmware)
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def algo(self) -> str | None:
|
|
||||||
if self.device_info.algo is not None:
|
|
||||||
return str(self.device_info.algo)
|
|
||||||
|
|
||||||
def keys(self) -> list:
|
|
||||||
return list(self.model_fields.keys())
|
|
||||||
|
|
||||||
def asdict(self) -> dict:
|
def asdict(self) -> dict:
|
||||||
return self.model_dump()
|
return asdict(self, dict_factory=self.dict_factory)
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
"""Get this dataclass as a dictionary.
|
"""Get this dataclass as a dictionary.
|
||||||
@@ -345,7 +307,9 @@ class MinerData(BaseModel):
|
|||||||
Returns:
|
Returns:
|
||||||
A JSON version of this class.
|
A JSON version of this class.
|
||||||
"""
|
"""
|
||||||
return self.model_dump_json()
|
data = self.asdict()
|
||||||
|
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
||||||
|
return json.dumps(data)
|
||||||
|
|
||||||
def as_csv(self) -> str:
|
def as_csv(self) -> str:
|
||||||
"""Get this dataclass as CSV.
|
"""Get this dataclass as CSV.
|
||||||
@@ -354,6 +318,7 @@ class MinerData(BaseModel):
|
|||||||
A CSV version of this class with no headers.
|
A CSV version of this class with no headers.
|
||||||
"""
|
"""
|
||||||
data = self.asdict()
|
data = self.asdict()
|
||||||
|
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
||||||
errs = []
|
errs = []
|
||||||
for error in data["errors"]:
|
for error in data["errors"]:
|
||||||
errs.append(error["error_message"])
|
errs.append(error["error_message"])
|
||||||
@@ -361,9 +326,7 @@ class MinerData(BaseModel):
|
|||||||
data_list = [str(data[item]) for item in data]
|
data_list = [str(data[item]) for item in data]
|
||||||
return ",".join(data_list)
|
return ",".join(data_list)
|
||||||
|
|
||||||
def as_influxdb(
|
def as_influxdb(self, measurement_name: str = "miner_data") -> str:
|
||||||
self, measurement_name: str = "miner_data", level_delimiter: str = "."
|
|
||||||
) -> str:
|
|
||||||
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
|
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -372,127 +335,54 @@ class MinerData(BaseModel):
|
|||||||
Returns:
|
Returns:
|
||||||
A influxdb line protocol version of this class.
|
A influxdb line protocol version of this class.
|
||||||
"""
|
"""
|
||||||
|
tag_data = [measurement_name]
|
||||||
def serialize_int(key: str, value: int) -> str:
|
|
||||||
return f"{key}={value}"
|
|
||||||
|
|
||||||
def serialize_float(key: str, value: float) -> str:
|
|
||||||
return f"{key}={value}"
|
|
||||||
|
|
||||||
def serialize_str(key: str, value: str) -> str:
|
|
||||||
return f'{key}="{value}"'
|
|
||||||
|
|
||||||
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
|
|
||||||
return f"{key}={round(float(value), 2)}"
|
|
||||||
|
|
||||||
def serialize_list(key: str, value: list[Any]) -> str | None:
|
|
||||||
if len(value) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
list_field_data = []
|
|
||||||
for idx, list_field_val in enumerate(value):
|
|
||||||
item_serialization_func = serialization_map.get(
|
|
||||||
type(list_field_val), lambda _k, _v: None
|
|
||||||
)
|
|
||||||
item_serialized = item_serialization_func(
|
|
||||||
f"{key}{level_delimiter}{idx}", list_field_val
|
|
||||||
)
|
|
||||||
if item_serialized is not None:
|
|
||||||
list_field_data.append(item_serialized)
|
|
||||||
continue
|
|
||||||
for dt in serialization_map_instance:
|
|
||||||
if item_serialized is None:
|
|
||||||
if isinstance(list_field_val, dt):
|
|
||||||
item_serialized = serialization_map_instance[dt](
|
|
||||||
f"{key}{level_delimiter}{idx}", list_field_val
|
|
||||||
)
|
|
||||||
if item_serialized is not None:
|
|
||||||
list_field_data.append(item_serialized)
|
|
||||||
return ",".join(list_field_data)
|
|
||||||
|
|
||||||
def serialize_miner_error(key: str, value: BaseMinerError):
|
|
||||||
return value.as_influxdb(key, level_delimiter=level_delimiter)
|
|
||||||
|
|
||||||
def serialize_fan(key: str, value: Fan) -> str:
|
|
||||||
return f"{key}{level_delimiter}speed={value.speed}"
|
|
||||||
|
|
||||||
def serialize_hashboard(key: str, value: HashBoard) -> str:
|
|
||||||
return value.as_influxdb(key, level_delimiter=level_delimiter)
|
|
||||||
|
|
||||||
def serialize_bool(key: str, value: bool):
|
|
||||||
return f"{key}={str(value).lower()}"
|
|
||||||
|
|
||||||
def serialize_pool_metrics(key: str, value: PoolMetrics):
|
|
||||||
return value.as_influxdb(key, level_delimiter=level_delimiter)
|
|
||||||
|
|
||||||
include = [
|
|
||||||
"uptime",
|
|
||||||
"expected_hashrate",
|
|
||||||
"hashrate",
|
|
||||||
"hashboards",
|
|
||||||
"temperature_avg",
|
|
||||||
"env_temp",
|
|
||||||
"wattage",
|
|
||||||
"wattage_limit",
|
|
||||||
"voltage",
|
|
||||||
"fans",
|
|
||||||
"expected_fans",
|
|
||||||
"fan_psu",
|
|
||||||
"total_chips",
|
|
||||||
"expected_chips",
|
|
||||||
"efficiency",
|
|
||||||
"fault_light",
|
|
||||||
"is_mining",
|
|
||||||
"errors",
|
|
||||||
"pools",
|
|
||||||
]
|
|
||||||
|
|
||||||
serialization_map_instance = {
|
|
||||||
AlgoHashRateType: serialize_algo_hash_rate,
|
|
||||||
BaseMinerError: serialize_miner_error,
|
|
||||||
}
|
|
||||||
serialization_map = {
|
|
||||||
int: serialize_int,
|
|
||||||
float: serialize_float,
|
|
||||||
str: serialize_str,
|
|
||||||
bool: serialize_bool,
|
|
||||||
list: serialize_list,
|
|
||||||
Fan: serialize_fan,
|
|
||||||
HashBoard: serialize_hashboard,
|
|
||||||
PoolMetrics: serialize_pool_metrics,
|
|
||||||
}
|
|
||||||
|
|
||||||
tag_data = [
|
|
||||||
measurement_name,
|
|
||||||
f"ip={str(self.ip)}",
|
|
||||||
f"mac={str(self.mac)}",
|
|
||||||
f"make={str(self.make)}",
|
|
||||||
f"model={str(self.model)}",
|
|
||||||
f"firmware={str(self.firmware)}",
|
|
||||||
f"algo={str(self.algo)}",
|
|
||||||
]
|
|
||||||
field_data = []
|
field_data = []
|
||||||
|
|
||||||
for field in include:
|
tags = ["ip", "mac", "model", "hostname"]
|
||||||
field_val = getattr(self, field)
|
for attribute in self:
|
||||||
serialization_func = serialization_map.get(
|
if attribute in tags:
|
||||||
type(field_val), lambda _k, _v: None
|
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
|
||||||
)
|
tag_data.append(f"{attribute}={escaped_data}")
|
||||||
serialized = serialization_func(field, field_val)
|
|
||||||
if serialized is not None:
|
|
||||||
field_data.append(serialized)
|
|
||||||
continue
|
continue
|
||||||
for datatype in serialization_map_instance:
|
elif str(attribute).startswith("_"):
|
||||||
if serialized is None:
|
continue
|
||||||
if isinstance(field_val, datatype):
|
elif isinstance(self[attribute], str):
|
||||||
serialized = serialization_map_instance[datatype](
|
field_data.append(f'{attribute}="{self[attribute]}"')
|
||||||
field, field_val
|
continue
|
||||||
|
elif isinstance(self[attribute], bool):
|
||||||
|
field_data.append(f"{attribute}={str(self[attribute]).lower()}")
|
||||||
|
continue
|
||||||
|
elif isinstance(self[attribute], int):
|
||||||
|
field_data.append(f"{attribute}={self[attribute]}")
|
||||||
|
continue
|
||||||
|
elif isinstance(self[attribute], float):
|
||||||
|
field_data.append(f"{attribute}={self[attribute]}")
|
||||||
|
continue
|
||||||
|
elif attribute == "errors":
|
||||||
|
for idx, item in enumerate(self[attribute]):
|
||||||
|
field_data.append(f'error_{idx+1}="{item.error_message}"')
|
||||||
|
elif attribute == "hashboards":
|
||||||
|
for idx, item in enumerate(self[attribute]):
|
||||||
|
field_data.append(
|
||||||
|
f"hashboard_{idx+1}_hashrate={item.get('hashrate', 0.0)}"
|
||||||
)
|
)
|
||||||
if serialized is not None:
|
field_data.append(
|
||||||
field_data.append(serialized)
|
f"hashboard_{idx+1}_temperature={item.get('temp', 0)}"
|
||||||
|
)
|
||||||
|
field_data.append(
|
||||||
|
f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}"
|
||||||
|
)
|
||||||
|
field_data.append(f"hashboard_{idx+1}_chips={item.get('chips', 0)}")
|
||||||
|
field_data.append(
|
||||||
|
f"hashboard_{idx+1}_expected_chips={item.get('expected_chips', 0)}"
|
||||||
|
)
|
||||||
|
elif attribute == "fans":
|
||||||
|
for idx, item in enumerate(self[attribute]):
|
||||||
|
if item.speed is not None:
|
||||||
|
field_data.append(f"fan_{idx+1}={item.speed}")
|
||||||
|
|
||||||
tags_str = ",".join(tag_data).replace(" ", "\\ ")
|
tags_str = ",".join(tag_data)
|
||||||
field_str = ",".join(field_data).replace(" ", "\\ ")
|
field_str = ",".join(field_data)
|
||||||
timestamp = str(self.timestamp * 10**9)
|
timestamp = str(int(time.mktime(self.datetime.timetuple()) * 1e9))
|
||||||
|
|
||||||
return " ".join([tags_str, field_str, timestamp])
|
return " ".join([tags_str, field_str, timestamp])
|
||||||
|
|||||||
@@ -13,16 +13,13 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate import AlgoHashRateType
|
@dataclass
|
||||||
|
class HashBoard:
|
||||||
|
|
||||||
class HashBoard(BaseModel):
|
|
||||||
"""A Dataclass to standardize hashboard data.
|
"""A Dataclass to standardize hashboard data.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -34,28 +31,16 @@ class HashBoard(BaseModel):
|
|||||||
expected_chips: The expected chip count of the board as an int.
|
expected_chips: The expected chip count of the board as an int.
|
||||||
serial_number: The serial number of the board.
|
serial_number: The serial number of the board.
|
||||||
missing: Whether the board is returned from the miners data as a bool.
|
missing: Whether the board is returned from the miners data as a bool.
|
||||||
tuned: Whether the board is tuned as a bool.
|
|
||||||
active: Whether the board is currently tuning as a bool.
|
|
||||||
voltage: Current input voltage of the board as a float.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
slot: int = 0
|
slot: int = 0
|
||||||
hashrate: AlgoHashRateType | None = None
|
hashrate: float = None
|
||||||
temp: float | None = None
|
temp: int = None
|
||||||
chip_temp: float | None = None
|
chip_temp: int = None
|
||||||
chips: int | None = None
|
chips: int = None
|
||||||
expected_chips: int | None = None
|
expected_chips: int = None
|
||||||
serial_number: str | None = None
|
serial_number: str = None
|
||||||
missing: bool = True
|
missing: bool = True
|
||||||
tuned: bool | None = None
|
|
||||||
active: bool | None = None
|
|
||||||
voltage: float | None = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fields(cls) -> set:
|
|
||||||
all_fields = set(cls.model_fields.keys())
|
|
||||||
all_fields.update(set(cls.model_computed_fields.keys()))
|
|
||||||
return all_fields
|
|
||||||
|
|
||||||
def get(self, __key: str, default: Any = None):
|
def get(self, __key: str, default: Any = None):
|
||||||
try:
|
try:
|
||||||
@@ -71,63 +56,3 @@ class HashBoard(BaseModel):
|
|||||||
return getattr(self, item)
|
return getattr(self, item)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise KeyError(f"{item}")
|
raise KeyError(f"{item}")
|
||||||
|
|
||||||
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
|
|
||||||
|
|
||||||
def serialize_int(key: str, value: int) -> str:
|
|
||||||
return f"{key}={value}"
|
|
||||||
|
|
||||||
def serialize_float(key: str, value: float) -> str:
|
|
||||||
return f"{key}={value}"
|
|
||||||
|
|
||||||
def serialize_str(key: str, value: str) -> str:
|
|
||||||
return f'{key}="{value}"'
|
|
||||||
|
|
||||||
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
|
|
||||||
return f"{key}={round(float(value), 2)}"
|
|
||||||
|
|
||||||
def serialize_bool(key: str, value: bool):
|
|
||||||
return f"{key}={str(value).lower()}"
|
|
||||||
|
|
||||||
serialization_map_instance = {
|
|
||||||
AlgoHashRateType: serialize_algo_hash_rate,
|
|
||||||
}
|
|
||||||
serialization_map = {
|
|
||||||
int: serialize_int,
|
|
||||||
float: serialize_float,
|
|
||||||
str: serialize_str,
|
|
||||||
bool: serialize_bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
include = [
|
|
||||||
"hashrate",
|
|
||||||
"temp",
|
|
||||||
"chip_temp",
|
|
||||||
"chips",
|
|
||||||
"expected_chips",
|
|
||||||
"tuned",
|
|
||||||
"active",
|
|
||||||
"voltage",
|
|
||||||
]
|
|
||||||
|
|
||||||
field_data = []
|
|
||||||
for field in include:
|
|
||||||
field_val = getattr(self, field)
|
|
||||||
serialization_func = serialization_map.get(
|
|
||||||
type(field_val), lambda _k, _v: None
|
|
||||||
)
|
|
||||||
serialized = serialization_func(
|
|
||||||
f"{key_root}{level_delimiter}{field}", field_val
|
|
||||||
)
|
|
||||||
if serialized is not None:
|
|
||||||
field_data.append(serialized)
|
|
||||||
continue
|
|
||||||
for datatype in serialization_map_instance:
|
|
||||||
if serialized is None:
|
|
||||||
if isinstance(field_val, datatype):
|
|
||||||
serialized = serialization_map_instance[datatype](
|
|
||||||
f"{key_root}{level_delimiter}{field}", field_val
|
|
||||||
)
|
|
||||||
if serialized is not None:
|
|
||||||
field_data.append(serialized)
|
|
||||||
return ",".join(field_data)
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
from pydantic import BaseModel, ConfigDict, field_serializer
|
|
||||||
|
|
||||||
from pyasic.device.algorithm import MinerAlgoType
|
|
||||||
from pyasic.device.firmware import MinerFirmware
|
|
||||||
from pyasic.device.makes import MinerMake
|
|
||||||
from pyasic.device.models import MinerModelType
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceInfo(BaseModel):
|
|
||||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
||||||
|
|
||||||
make: MinerMake | None = None
|
|
||||||
model: MinerModelType | None = None
|
|
||||||
firmware: MinerFirmware | None = None
|
|
||||||
algo: type[MinerAlgoType] | None = None
|
|
||||||
|
|
||||||
@field_serializer("make")
|
|
||||||
def serialize_make(self, make: MinerMake, _info):
|
|
||||||
return str(make)
|
|
||||||
|
|
||||||
@field_serializer("model")
|
|
||||||
def serialize_model(self, model: MinerModelType, _info):
|
|
||||||
return str(model)
|
|
||||||
|
|
||||||
@field_serializer("firmware")
|
|
||||||
def serialize_firmware(self, firmware: MinerFirmware, _info):
|
|
||||||
return str(firmware)
|
|
||||||
|
|
||||||
@field_serializer("algo")
|
|
||||||
def serialize_algo(self, algo: MinerAlgoType, _info):
|
|
||||||
return str(algo)
|
|
||||||
@@ -13,10 +13,12 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from pyasic.data.error_codes.base import BaseMinerError
|
|
||||||
|
from dataclasses import asdict, dataclass, fields
|
||||||
|
|
||||||
|
|
||||||
class X19Error(BaseMinerError):
|
@dataclass
|
||||||
|
class X19Error:
|
||||||
"""A Dataclass to handle error codes of X19 miners.
|
"""A Dataclass to handle error codes of X19 miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -26,3 +28,10 @@ class X19Error(BaseMinerError):
|
|||||||
|
|
||||||
error_message: str
|
error_message: str
|
||||||
error_code: int = 0
|
error_code: int = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fields(cls):
|
||||||
|
return fields(cls)
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return asdict(self)
|
||||||
|
|||||||
@@ -18,15 +18,9 @@ from typing import TypeVar
|
|||||||
|
|
||||||
from .bos import BraiinsOSError
|
from .bos import BraiinsOSError
|
||||||
from .innosilicon import InnosiliconError
|
from .innosilicon import InnosiliconError
|
||||||
from .vnish import VnishError
|
|
||||||
from .whatsminer import WhatsminerError
|
from .whatsminer import WhatsminerError
|
||||||
from .X19 import X19Error
|
from .X19 import X19Error
|
||||||
|
|
||||||
MinerErrorData = TypeVar(
|
MinerErrorData = TypeVar(
|
||||||
"MinerErrorData",
|
"MinerErrorData", WhatsminerError, BraiinsOSError, X19Error, InnosiliconError
|
||||||
WhatsminerError,
|
|
||||||
BraiinsOSError,
|
|
||||||
X19Error,
|
|
||||||
InnosiliconError,
|
|
||||||
VnishError,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class BaseMinerError(BaseModel):
|
|
||||||
@classmethod
|
|
||||||
def fields(cls):
|
|
||||||
return list(cls.model_fields.keys())
|
|
||||||
|
|
||||||
def asdict(self) -> dict:
|
|
||||||
return self.model_dump()
|
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
|
||||||
"""Get this dataclass as a dictionary.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A dictionary version of this class.
|
|
||||||
"""
|
|
||||||
return self.asdict()
|
|
||||||
|
|
||||||
def as_influxdb(self, root_key: str, level_delimiter: str = ".") -> str:
|
|
||||||
field_data = []
|
|
||||||
|
|
||||||
if self.error_code is not None:
|
|
||||||
field_data.append(
|
|
||||||
f"{root_key}{level_delimiter}error_code={self.error_code}"
|
|
||||||
)
|
|
||||||
if self.error_message is not None:
|
|
||||||
field_data.append(
|
|
||||||
f'{root_key}{level_delimiter}error_message="{self.error_message}"'
|
|
||||||
)
|
|
||||||
|
|
||||||
return ",".join(field_data)
|
|
||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.data.error_codes.base import BaseMinerError
|
from dataclasses import asdict, dataclass, fields
|
||||||
|
|
||||||
|
|
||||||
class BraiinsOSError(BaseMinerError):
|
@dataclass
|
||||||
|
class BraiinsOSError:
|
||||||
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -27,3 +28,10 @@ class BraiinsOSError(BaseMinerError):
|
|||||||
|
|
||||||
error_message: str
|
error_message: str
|
||||||
error_code: int = 0
|
error_code: int = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fields(cls):
|
||||||
|
return fields(cls)
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return asdict(self)
|
||||||
|
|||||||
@@ -14,13 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from dataclasses import asdict, dataclass, field, fields
|
||||||
from pydantic import computed_field
|
|
||||||
|
|
||||||
from pyasic.data.error_codes.base import BaseMinerError
|
|
||||||
|
|
||||||
|
|
||||||
class InnosiliconError(BaseMinerError):
|
@dataclass
|
||||||
|
class InnosiliconError:
|
||||||
"""A Dataclass to handle error codes of Innosilicon miners.
|
"""A Dataclass to handle error codes of Innosilicon miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -29,14 +27,25 @@ class InnosiliconError(BaseMinerError):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
error_code: int
|
error_code: int
|
||||||
|
error_message: str = field(init=False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fields(cls):
|
||||||
|
return fields(cls)
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def error_message(self) -> str: # noqa - Skip PyCharm inspection
|
def error_message(self): # noqa - Skip PyCharm inspection
|
||||||
if self.error_code in ERROR_CODES:
|
if self.error_code in ERROR_CODES:
|
||||||
return ERROR_CODES[self.error_code]
|
return ERROR_CODES[self.error_code]
|
||||||
return "Unknown error type."
|
return "Unknown error type."
|
||||||
|
|
||||||
|
@error_message.setter
|
||||||
|
def error_message(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
ERROR_CODES = {
|
ERROR_CODES = {
|
||||||
21: "The PLUG signal of the hash board is not detected.",
|
21: "The PLUG signal of the hash board is not detected.",
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from pydantic import computed_field
|
|
||||||
|
|
||||||
from pyasic.data.error_codes.base import BaseMinerError
|
from dataclasses import asdict, dataclass, field, fields
|
||||||
|
|
||||||
|
|
||||||
class WhatsminerError(BaseMinerError):
|
@dataclass
|
||||||
|
class WhatsminerError:
|
||||||
"""A Dataclass to handle error codes of Whatsminers.
|
"""A Dataclass to handle error codes of Whatsminers.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -27,10 +27,14 @@ class WhatsminerError(BaseMinerError):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
error_code: int
|
error_code: int
|
||||||
|
error_message: str = field(init=False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fields(cls):
|
||||||
|
return fields(cls)
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
@property
|
||||||
def error_message(self) -> str: # noqa - Skip PyCharm inspection
|
def error_message(self): # noqa - Skip PyCharm inspection
|
||||||
if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1":
|
if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1":
|
||||||
err_type = int(str(self.error_code)[:2])
|
err_type = int(str(self.error_code)[:2])
|
||||||
err_subtype = int(str(self.error_code)[2:3])
|
err_subtype = int(str(self.error_code)[2:3])
|
||||||
@@ -70,6 +74,13 @@ class WhatsminerError(BaseMinerError):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return "Unknown error type."
|
return "Unknown error type."
|
||||||
|
|
||||||
|
@error_message.setter
|
||||||
|
def error_message(self, val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
ERROR_CODES = {
|
ERROR_CODES = {
|
||||||
1: { # Fan error
|
1: { # Fan error
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class Fan(BaseModel):
|
class Fan:
|
||||||
"""A Dataclass to standardize fan data.
|
"""A Dataclass to standardize fan data.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
from enum import Enum
|
|
||||||
from typing import Optional
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from pydantic import BaseModel, computed_field, model_serializer
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
|
|
||||||
class Scheme(Enum):
|
|
||||||
STRATUM_V1 = "stratum+tcp"
|
|
||||||
STRATUM_V2 = "stratum2+tcp"
|
|
||||||
STRATUM_V1_SSL = "stratum+ssl"
|
|
||||||
|
|
||||||
|
|
||||||
class PoolUrl(BaseModel):
|
|
||||||
scheme: Scheme
|
|
||||||
host: str
|
|
||||||
port: int
|
|
||||||
pubkey: Optional[str] = None
|
|
||||||
|
|
||||||
@model_serializer
|
|
||||||
def serialize(self):
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
if self.scheme == Scheme.STRATUM_V2 and self.pubkey:
|
|
||||||
return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}"
|
|
||||||
else:
|
|
||||||
return f"{self.scheme.value}://{self.host}:{self.port}"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_str(cls, url: str) -> Self | None:
|
|
||||||
parsed_url = urlparse(url)
|
|
||||||
if not parsed_url.hostname:
|
|
||||||
return None
|
|
||||||
if not parsed_url.scheme.strip() == "":
|
|
||||||
scheme = Scheme(parsed_url.scheme)
|
|
||||||
else:
|
|
||||||
scheme = Scheme.STRATUM_V1
|
|
||||||
host = parsed_url.hostname
|
|
||||||
port = parsed_url.port
|
|
||||||
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
|
|
||||||
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
|
|
||||||
|
|
||||||
|
|
||||||
class PoolMetrics(BaseModel):
|
|
||||||
"""A dataclass to standardize pool metrics returned from miners.
|
|
||||||
Attributes:
|
|
||||||
|
|
||||||
accepted: Number of accepted shares.
|
|
||||||
rejected: Number of rejected shares.
|
|
||||||
get_failures: Number of failures in obtaining work from the pool.
|
|
||||||
remote_failures: Number of failures communicating with the pool server.
|
|
||||||
active: Indicates if the miner is connected to the stratum server.
|
|
||||||
Alive : Indicates if a pool is alive.
|
|
||||||
url: URL of the pool.
|
|
||||||
index: Index of the pool.
|
|
||||||
user: Username for the pool.
|
|
||||||
pool_rejected_percent: Percentage of rejected shares by the pool.
|
|
||||||
pool_stale_percent: Percentage of stale shares by the pool.
|
|
||||||
"""
|
|
||||||
|
|
||||||
url: PoolUrl | None
|
|
||||||
accepted: int | None = None
|
|
||||||
rejected: int | None = None
|
|
||||||
get_failures: int | None = None
|
|
||||||
remote_failures: int | None = None
|
|
||||||
active: bool | None = None
|
|
||||||
alive: bool | None = None
|
|
||||||
index: int | None = None
|
|
||||||
user: str | None = None
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
|
|
||||||
"""Calculate and return the percentage of rejected shares"""
|
|
||||||
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
|
|
||||||
|
|
||||||
@computed_field # type: ignore[misc]
|
|
||||||
@property
|
|
||||||
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
|
|
||||||
"""Calculate and return the percentage of stale shares."""
|
|
||||||
return self._calculate_percentage(
|
|
||||||
self.get_failures, self.accepted + self.rejected
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _calculate_percentage(value: int, total: int) -> float:
|
|
||||||
"""Calculate the percentage."""
|
|
||||||
if value is None or total is None:
|
|
||||||
return 0
|
|
||||||
if total == 0:
|
|
||||||
return 0
|
|
||||||
return (value / total) * 100
|
|
||||||
|
|
||||||
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
|
|
||||||
|
|
||||||
def serialize_int(key: str, value: int) -> str:
|
|
||||||
return f"{key}={value}"
|
|
||||||
|
|
||||||
def serialize_float(key: str, value: float) -> str:
|
|
||||||
return f"{key}={value}"
|
|
||||||
|
|
||||||
def serialize_str(key: str, value: str) -> str:
|
|
||||||
return f'{key}="{value}"'
|
|
||||||
|
|
||||||
def serialize_pool_url(key: str, value: str) -> str:
|
|
||||||
return f'{key}="{str(value)}"'
|
|
||||||
|
|
||||||
def serialize_bool(key: str, value: bool):
|
|
||||||
return f"{key}={str(value).lower()}"
|
|
||||||
|
|
||||||
serialization_map = {
|
|
||||||
int: serialize_int,
|
|
||||||
float: serialize_float,
|
|
||||||
str: serialize_str,
|
|
||||||
bool: serialize_bool,
|
|
||||||
PoolUrl: serialize_pool_url,
|
|
||||||
}
|
|
||||||
|
|
||||||
include = [
|
|
||||||
"url",
|
|
||||||
"accepted",
|
|
||||||
"rejected",
|
|
||||||
"active",
|
|
||||||
"alive",
|
|
||||||
"user",
|
|
||||||
]
|
|
||||||
|
|
||||||
field_data = []
|
|
||||||
for field in include:
|
|
||||||
field_val = getattr(self, field)
|
|
||||||
serialization_func = serialization_map.get(
|
|
||||||
type(field_val), lambda _k, _v: None
|
|
||||||
)
|
|
||||||
serialized = serialization_func(
|
|
||||||
f"{key_root}{level_delimiter}{field}", field_val
|
|
||||||
)
|
|
||||||
if serialized is not None:
|
|
||||||
field_data.append(serialized)
|
|
||||||
|
|
||||||
return ",".join(field_data)
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from .algorithm import MinerAlgo
|
|
||||||
from .firmware import MinerFirmware
|
|
||||||
from .makes import MinerMake
|
|
||||||
from .models import MinerModel
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
from .base import MinerAlgoType
|
|
||||||
from .blake256 import Blake256Algo
|
|
||||||
from .eaglesong import EaglesongAlgo
|
|
||||||
from .equihash import EquihashAlgo
|
|
||||||
from .ethash import EtHashAlgo
|
|
||||||
from .handshake import HandshakeAlgo
|
|
||||||
from .hashrate import *
|
|
||||||
from .hashrate.unit import *
|
|
||||||
from .kadena import KadenaAlgo
|
|
||||||
from .kheavyhash import KHeavyHashAlgo
|
|
||||||
from .scrypt import ScryptAlgo
|
|
||||||
from .sha256 import SHA256Algo
|
|
||||||
from .x11 import X11Algo
|
|
||||||
|
|
||||||
|
|
||||||
class MinerAlgo:
|
|
||||||
SHA256 = SHA256Algo
|
|
||||||
SCRYPT = ScryptAlgo
|
|
||||||
KHEAVYHASH = KHeavyHashAlgo
|
|
||||||
KADENA = KadenaAlgo
|
|
||||||
HANDSHAKE = HandshakeAlgo
|
|
||||||
X11 = X11Algo
|
|
||||||
BLAKE256 = Blake256Algo
|
|
||||||
EAGLESONG = EaglesongAlgo
|
|
||||||
ETHASH = EtHashAlgo
|
|
||||||
EQUIHASH = EquihashAlgo
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .hashrate.base import AlgoHashRateType, GenericHashrate
|
|
||||||
from .hashrate.unit.base import AlgoHashRateUnitType, GenericUnit
|
|
||||||
|
|
||||||
|
|
||||||
class MinerAlgoMeta(type):
|
|
||||||
name: str
|
|
||||||
|
|
||||||
def __str__(cls):
|
|
||||||
return cls.name
|
|
||||||
|
|
||||||
|
|
||||||
class MinerAlgoType(metaclass=MinerAlgoMeta):
|
|
||||||
hashrate: type[AlgoHashRateType]
|
|
||||||
unit: type[AlgoHashRateUnitType]
|
|
||||||
|
|
||||||
|
|
||||||
class GenericAlgo(MinerAlgoType):
|
|
||||||
hashrate: type[GenericHashrate] = GenericHashrate
|
|
||||||
unit: type[GenericUnit] = GenericUnit
|
|
||||||
|
|
||||||
name = "Generic (Unknown)"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import MinerAlgoType
|
|
||||||
from .hashrate import Blake256HashRate
|
|
||||||
from .hashrate.unit import Blake256Unit
|
|
||||||
|
|
||||||
|
|
||||||
# make this json serializable
|
|
||||||
class Blake256Algo(MinerAlgoType):
|
|
||||||
hashrate: type[Blake256HashRate] = Blake256HashRate
|
|
||||||
unit: type[Blake256Unit] = Blake256Unit
|
|
||||||
|
|
||||||
name = "Blake256"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import MinerAlgoType
|
|
||||||
from .hashrate import EaglesongHashRate
|
|
||||||
from .hashrate.unit import EaglesongUnit
|
|
||||||
|
|
||||||
|
|
||||||
# make this json serializable
|
|
||||||
class EaglesongAlgo(MinerAlgoType):
|
|
||||||
hashrate: type[EaglesongHashRate] = EaglesongHashRate
|
|
||||||
unit: type[EaglesongUnit] = EaglesongUnit
|
|
||||||
|
|
||||||
name = "Eaglesong"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import MinerAlgoType
|
|
||||||
from .hashrate import EquihashHashRate
|
|
||||||
from .hashrate.unit import EquihashUnit
|
|
||||||
|
|
||||||
|
|
||||||
# make this json serializable
|
|
||||||
class EquihashAlgo(MinerAlgoType):
|
|
||||||
hashrate: type[EquihashHashRate] = EquihashHashRate
|
|
||||||
unit: type[EquihashUnit] = EquihashUnit
|
|
||||||
|
|
||||||
name = "Equihash"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import MinerAlgoType
|
|
||||||
from .hashrate import EtHashHashRate
|
|
||||||
from .hashrate.unit import EtHashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class EtHashAlgo(MinerAlgoType):
|
|
||||||
hashrate: type[EtHashHashRate] = EtHashHashRate
|
|
||||||
unit: type[EtHashUnit] = EtHashUnit
|
|
||||||
|
|
||||||
name = "EtHash"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import MinerAlgoType
|
|
||||||
from .hashrate import HandshakeHashRate
|
|
||||||
from .hashrate.unit import HandshakeUnit
|
|
||||||
|
|
||||||
|
|
||||||
# make this json serializable
|
|
||||||
class HandshakeAlgo(MinerAlgoType):
|
|
||||||
hashrate: type[HandshakeHashRate] = HandshakeHashRate
|
|
||||||
unit: type[HandshakeUnit] = HandshakeUnit
|
|
||||||
|
|
||||||
name = "Handshake"
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
from .base import AlgoHashRateType
|
|
||||||
from .blake256 import Blake256HashRate
|
|
||||||
from .eaglesong import EaglesongHashRate
|
|
||||||
from .equihash import EquihashHashRate
|
|
||||||
from .ethash import EtHashHashRate
|
|
||||||
from .handshake import HandshakeHashRate
|
|
||||||
from .kadena import KadenaHashRate
|
|
||||||
from .kheavyhash import KHeavyHashHashRate
|
|
||||||
from .scrypt import ScryptHashRate
|
|
||||||
from .sha256 import SHA256HashRate
|
|
||||||
from .x11 import X11HashRate
|
|
||||||
|
|
||||||
|
|
||||||
class AlgoHashRate:
|
|
||||||
SHA256 = SHA256HashRate
|
|
||||||
SCRYPT = ScryptHashRate
|
|
||||||
KHEAVYHASH = KHeavyHashHashRate
|
|
||||||
KADENA = KadenaHashRate
|
|
||||||
HANDSHAKE = HandshakeHashRate
|
|
||||||
X11 = X11HashRate
|
|
||||||
BLAKE256 = Blake256HashRate
|
|
||||||
EAGLESONG = EaglesongHashRate
|
|
||||||
ETHASH = EtHashHashRate
|
|
||||||
EQUIHASH = EquihashHashRate
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
from pydantic import BaseModel, field_serializer
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from .unit.base import AlgoHashRateUnitType, GenericUnit
|
|
||||||
|
|
||||||
|
|
||||||
class AlgoHashRateType(BaseModel, ABC):
|
|
||||||
unit: AlgoHashRateUnitType
|
|
||||||
rate: float
|
|
||||||
|
|
||||||
@field_serializer("unit")
|
|
||||||
def serialize_unit(self, unit: AlgoHashRateUnitType):
|
|
||||||
return unit.model_dump()
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def into(self, other: "AlgoHashRateUnitType"):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def auto_unit(self):
|
|
||||||
if 1 < self.rate // int(self.unit.H) < 1000:
|
|
||||||
return self.into(self.unit.H)
|
|
||||||
if 1 < self.rate // int(self.unit.MH) < 1000:
|
|
||||||
return self.into(self.unit.MH)
|
|
||||||
if 1 < self.rate // int(self.unit.GH) < 1000:
|
|
||||||
return self.into(self.unit.GH)
|
|
||||||
if 1 < self.rate // int(self.unit.TH) < 1000:
|
|
||||||
return self.into(self.unit.TH)
|
|
||||||
if 1 < self.rate // int(self.unit.PH) < 1000:
|
|
||||||
return self.into(self.unit.PH)
|
|
||||||
if 1 < self.rate // int(self.unit.EH) < 1000:
|
|
||||||
return self.into(self.unit.EH)
|
|
||||||
if 1 < self.rate // int(self.unit.ZH) < 1000:
|
|
||||||
return self.into(self.unit.ZH)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __float__(self):
|
|
||||||
return float(self.rate)
|
|
||||||
|
|
||||||
def __int__(self):
|
|
||||||
return int(self.rate)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"{self.rate} {str(self.unit)}"
|
|
||||||
|
|
||||||
def __round__(self, n: int = None):
|
|
||||||
return round(self.rate, n)
|
|
||||||
|
|
||||||
def __add__(self, other: Self | int | float) -> Self:
|
|
||||||
if isinstance(other, AlgoHashRateType):
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
|
||||||
)
|
|
||||||
return self.__class__(rate=self.rate + other, unit=self.unit)
|
|
||||||
|
|
||||||
def __sub__(self, other: Self | int | float) -> Self:
|
|
||||||
if isinstance(other, AlgoHashRateType):
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
|
||||||
)
|
|
||||||
return self.__class__(rate=self.rate - other, unit=self.unit)
|
|
||||||
|
|
||||||
def __truediv__(self, other: Self | int | float) -> Self:
|
|
||||||
if isinstance(other, AlgoHashRateType):
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
|
||||||
)
|
|
||||||
return self.__class__(rate=self.rate / other, unit=self.unit)
|
|
||||||
|
|
||||||
def __floordiv__(self, other: Self | int | float) -> Self:
|
|
||||||
if isinstance(other, AlgoHashRateType):
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
|
||||||
)
|
|
||||||
return self.__class__(rate=self.rate // other, unit=self.unit)
|
|
||||||
|
|
||||||
def __mul__(self, other: Self | int | float) -> Self:
|
|
||||||
if isinstance(other, AlgoHashRateType):
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
|
||||||
)
|
|
||||||
return self.__class__(rate=self.rate * other, unit=self.unit)
|
|
||||||
|
|
||||||
|
|
||||||
class GenericHashrate(AlgoHashRateType):
|
|
||||||
rate: float = 0
|
|
||||||
unit: GenericUnit = GenericUnit.H
|
|
||||||
|
|
||||||
def into(self, other: GenericUnit):
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.blake256 import Blake256Unit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class Blake256HashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: Blake256Unit = HashUnit.BLAKE256.default
|
|
||||||
|
|
||||||
def into(self, other: Blake256Unit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.eaglesong import EaglesongUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class EaglesongHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: EaglesongUnit = HashUnit.EAGLESONG.default
|
|
||||||
|
|
||||||
def into(self, other: EaglesongUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.equihash import EquihashUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class EquihashHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: EquihashUnit = HashUnit.ETHASH.default
|
|
||||||
|
|
||||||
def into(self, other: EquihashUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.ethash import EtHashUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class EtHashHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: EtHashUnit = HashUnit.ETHASH.default
|
|
||||||
|
|
||||||
def into(self, other: EtHashUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.handshake import HandshakeUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class HandshakeHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: HandshakeUnit = HashUnit.HANDSHAKE.default
|
|
||||||
|
|
||||||
def into(self, other: HandshakeUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.kadena import KadenaUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class KadenaHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: KadenaUnit = HashUnit.KADENA.default
|
|
||||||
|
|
||||||
def into(self, other: KadenaUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.kheavyhash import KHeavyHashUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class KHeavyHashHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: KHeavyHashUnit = HashUnit.KHEAVYHASH.default
|
|
||||||
|
|
||||||
def into(self, other: KHeavyHashUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class ScryptHashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: ScryptUnit = HashUnit.SCRYPT.default
|
|
||||||
|
|
||||||
def into(self, other: ScryptUnit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
|
||||||
from pyasic.device.algorithm.hashrate.unit.sha256 import SHA256Unit
|
|
||||||
|
|
||||||
from .unit import HashUnit
|
|
||||||
|
|
||||||
|
|
||||||
class SHA256HashRate(AlgoHashRateType):
|
|
||||||
rate: float
|
|
||||||
unit: SHA256Unit = HashUnit.SHA256.default
|
|
||||||
|
|
||||||
def into(self, other: SHA256Unit) -> Self:
|
|
||||||
return self.__class__(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
from .blake256 import Blake256Unit
|
|
||||||
from .eaglesong import EaglesongUnit
|
|
||||||
from .equihash import EquihashUnit
|
|
||||||
from .ethash import EtHashUnit
|
|
||||||
from .handshake import HandshakeUnit
|
|
||||||
from .kadena import KadenaUnit
|
|
||||||
from .kheavyhash import KHeavyHashUnit
|
|
||||||
from .scrypt import ScryptUnit
|
|
||||||
from .sha256 import SHA256Unit
|
|
||||||
from .x11 import X11Unit
|
|
||||||
|
|
||||||
|
|
||||||
class HashUnit:
|
|
||||||
SHA256 = SHA256Unit
|
|
||||||
SCRYPT = ScryptUnit
|
|
||||||
KHEAVYHASH = KHeavyHashUnit
|
|
||||||
KADENA = KadenaUnit
|
|
||||||
HANDSHAKE = HandshakeUnit
|
|
||||||
X11 = X11Unit
|
|
||||||
BLAKE256 = Blake256Unit
|
|
||||||
EAGLESONG = EaglesongUnit
|
|
||||||
ETHASH = EtHashUnit
|
|
||||||
EQUIHASH = EquihashUnit
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
from enum import IntEnum
|
|
||||||
|
|
||||||
|
|
||||||
class AlgoHashRateUnitType(IntEnum):
|
|
||||||
H: int
|
|
||||||
KH: int
|
|
||||||
MH: int
|
|
||||||
GH: int
|
|
||||||
TH: int
|
|
||||||
PH: int
|
|
||||||
EH: int
|
|
||||||
ZH: int
|
|
||||||
|
|
||||||
default: int
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.value == self.H:
|
|
||||||
return "H/s"
|
|
||||||
if self.value == self.KH:
|
|
||||||
return "KH/s"
|
|
||||||
if self.value == self.MH:
|
|
||||||
return "MH/s"
|
|
||||||
if self.value == self.GH:
|
|
||||||
return "GH/s"
|
|
||||||
if self.value == self.TH:
|
|
||||||
return "TH/s"
|
|
||||||
if self.value == self.PH:
|
|
||||||
return "PH/s"
|
|
||||||
if self.value == self.EH:
|
|
||||||
return "EH/s"
|
|
||||||
if self.value == self.ZH:
|
|
||||||
return "ZH/s"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_str(cls, value: str):
|
|
||||||
if value == "H":
|
|
||||||
return cls.H
|
|
||||||
elif value == "KH":
|
|
||||||
return cls.KH
|
|
||||||
elif value == "MH":
|
|
||||||
return cls.MH
|
|
||||||
elif value == "GH":
|
|
||||||
return cls.GH
|
|
||||||
elif value == "TH":
|
|
||||||
return cls.TH
|
|
||||||
elif value == "PH":
|
|
||||||
return cls.PH
|
|
||||||
elif value == "EH":
|
|
||||||
return cls.EH
|
|
||||||
elif value == "ZH":
|
|
||||||
return cls.ZH
|
|
||||||
return cls.default
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
def model_dump(self):
|
|
||||||
return {"value": self.value, "suffix": str(self)}
|
|
||||||
|
|
||||||
|
|
||||||
class GenericUnit(AlgoHashRateUnitType):
|
|
||||||
H = 1
|
|
||||||
KH = int(H) * 1000
|
|
||||||
MH = int(KH) * 1000
|
|
||||||
GH = int(MH) * 1000
|
|
||||||
TH = int(GH) * 1000
|
|
||||||
PH = int(TH) * 1000
|
|
||||||
EH = int(PH) * 1000
|
|
||||||
ZH = int(EH) * 1000
|
|
||||||
|
|
||||||
default = H
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import AlgoHashRateUnitType
|
|
||||||
|
|
||||||
|
|
||||||
class Blake256Unit(AlgoHashRateUnitType):
|
|
||||||
H = 1
|
|
||||||
KH = int(H) * 1000
|
|
||||||
MH = int(KH) * 1000
|
|
||||||
GH = int(MH) * 1000
|
|
||||||
TH = int(GH) * 1000
|
|
||||||
PH = int(TH) * 1000
|
|
||||||
EH = int(PH) * 1000
|
|
||||||
ZH = int(EH) * 1000
|
|
||||||
|
|
||||||
default = TH
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import AlgoHashRateUnitType
|
|
||||||
|
|
||||||
|
|
||||||
class EaglesongUnit(AlgoHashRateUnitType):
|
|
||||||
H = 1
|
|
||||||
KH = int(H) * 1000
|
|
||||||
MH = int(KH) * 1000
|
|
||||||
GH = int(MH) * 1000
|
|
||||||
TH = int(GH) * 1000
|
|
||||||
PH = int(TH) * 1000
|
|
||||||
EH = int(PH) * 1000
|
|
||||||
ZH = int(EH) * 1000
|
|
||||||
|
|
||||||
default = TH
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import AlgoHashRateUnitType
|
|
||||||
|
|
||||||
|
|
||||||
class EquihashUnit(AlgoHashRateUnitType):
|
|
||||||
H = 1
|
|
||||||
KH = int(H) * 1000
|
|
||||||
MH = int(KH) * 1000
|
|
||||||
GH = int(MH) * 1000
|
|
||||||
TH = int(GH) * 1000
|
|
||||||
PH = int(TH) * 1000
|
|
||||||
EH = int(PH) * 1000
|
|
||||||
ZH = int(EH) * 1000
|
|
||||||
|
|
||||||
default = KH
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.value == self.H:
|
|
||||||
return "Sol/s"
|
|
||||||
if self.value == self.KH:
|
|
||||||
return "KSol/s"
|
|
||||||
if self.value == self.MH:
|
|
||||||
return "MSol/s"
|
|
||||||
if self.value == self.GH:
|
|
||||||
return "GSol/s"
|
|
||||||
if self.value == self.TH:
|
|
||||||
return "TSol/s"
|
|
||||||
if self.value == self.PH:
|
|
||||||
return "PSol/s"
|
|
||||||
if self.value == self.EH:
|
|
||||||
return "ESol/s"
|
|
||||||
if self.value == self.ZH:
|
|
||||||
return "ZSol/s"
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .base import AlgoHashRateUnitType
|
|
||||||
|
|
||||||
|
|
||||||
class EtHashUnit(AlgoHashRateUnitType):
|
|
||||||
H = 1
|
|
||||||
KH = int(H) * 1000
|
|
||||||
MH = int(KH) * 1000
|
|
||||||
GH = int(MH) * 1000
|
|
||||||
TH = int(GH) * 1000
|
|
||||||
PH = int(TH) * 1000
|
|
||||||
EH = int(PH) * 1000
|
|
||||||
ZH = int(EH) * 1000
|
|
||||||
|
|
||||||
default = MH
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user