Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55786b154d | ||
|
|
6ab9681dec | ||
|
|
89641c6316 | ||
|
|
136ff6a688 | ||
|
|
d918d93f4a | ||
|
|
8046c532a6 | ||
|
|
92820a362d | ||
|
|
9fd90031a9 | ||
|
|
2f2223a112 | ||
|
|
50e6cf9dfd | ||
|
|
1b5e3093e6 | ||
|
|
9e3578b4a2 | ||
|
|
a3087e1a96 | ||
|
|
4b16ea2ca2 | ||
|
|
5dd361c4ef | ||
|
|
098112742c | ||
|
|
cd31e0743e | ||
|
|
1a7d0bf7cc | ||
|
|
41b5ebf0f0 | ||
|
|
5436bede29 | ||
|
|
c01b3958dc | ||
|
|
c16367285f | ||
|
|
17eae253e6 | ||
|
|
ab30988614 | ||
|
|
9b8e547f86 | ||
|
|
3b8cbb9ff1 | ||
|
|
d39d278296 | ||
|
|
ea8b922367 | ||
|
|
0d1c8d80e0 | ||
|
|
494d25da97 | ||
|
|
0327d93a35 | ||
|
|
680584c468 | ||
|
|
c0dbafb198 | ||
|
|
97d2c4ac34 | ||
|
|
055d633c91 | ||
|
|
68c57f265f | ||
|
|
1ba0f8ed83 | ||
|
|
7f74b083d3 | ||
|
|
97c20dae0a | ||
|
|
09c7aff640 | ||
|
|
7e7cdc9615 | ||
|
|
b6fb0fd2b9 | ||
|
|
46788e7d14 | ||
|
|
5b4f84a241 | ||
|
|
0c56bfdf9e | ||
|
|
4a5d793cd6 | ||
|
|
1894ff1aea | ||
|
|
3ab9294000 | ||
|
|
5e0641634b | ||
|
|
a1975bc9b8 | ||
|
|
6a265f03f7 | ||
|
|
c3658f028f | ||
|
|
ba3c653a29 | ||
|
|
61fbc132ed | ||
|
|
3f9f232990 | ||
|
|
29c2398846 | ||
|
|
ecc161820d | ||
|
|
5fec3052f6 | ||
|
|
437ee774ab | ||
|
|
aed9e0e406 | ||
|
|
be96428823 | ||
|
|
446881b237 | ||
|
|
ceab8e55b5 | ||
|
|
e12f85c94d | ||
|
|
0c85f53177 | ||
|
|
0b524f9bd0 | ||
|
|
95db852636 | ||
|
|
93fa02412a | ||
|
|
edb77e9cd8 | ||
|
|
cbf7eeb08d | ||
|
|
d34b35a82d | ||
|
|
5d57f35475 | ||
|
|
c95491ea45 | ||
|
|
e9ec43fac9 | ||
|
|
42bde081c4 | ||
|
|
bfb72aec1b | ||
|
|
b2f36b2f0b | ||
|
|
f75c07401b | ||
|
|
01b96227e0 | ||
|
|
82552390c8 | ||
|
|
b0651e26b8 | ||
|
|
c00802e311 | ||
|
|
d66739e2c9 | ||
|
|
65ed565220 | ||
|
|
db6499800b | ||
|
|
cc97ceee61 | ||
|
|
3fa1cb18d9 | ||
|
|
cb3c50d007 | ||
|
|
2523ef8484 | ||
|
|
01342738b0 | ||
|
|
a9dee4a911 | ||
|
|
883ffe20b4 | ||
|
|
261527a380 | ||
|
|
924b62e0d5 | ||
|
|
76a870c2ed | ||
|
|
309356243b | ||
|
|
e9b4cc9bd6 | ||
|
|
648c54de93 | ||
|
|
e1ce96ab1b | ||
|
|
86860a8dc4 | ||
|
|
5212641f45 | ||
|
|
52432e6043 | ||
|
|
727e484860 | ||
|
|
6c091756d2 | ||
|
|
14533ce4fe | ||
|
|
82d1840039 | ||
|
|
8e6240cdba | ||
|
|
5749e173d1 | ||
|
|
7d682b62ac | ||
|
|
6739a1001f | ||
|
|
56e4a5307f | ||
|
|
88de27c9e7 | ||
|
|
a77113c4db | ||
|
|
c19945bb82 | ||
|
|
1756937d20 | ||
|
|
c7b7fe864b | ||
|
|
e7ebefd1bf | ||
|
|
4677efbc46 | ||
|
|
4b7a1a0495 | ||
|
|
cc4e7da4e5 | ||
|
|
a3d2d7d35e | ||
|
|
d67de98bd0 | ||
|
|
fd1a3e459b | ||
|
|
adcab694b5 | ||
|
|
2bb097272f | ||
|
|
896968dded | ||
|
|
56b8f7c5b3 | ||
|
|
0ed7559aef | ||
|
|
275d87e4fe | ||
|
|
c3ab814d77 | ||
|
|
05a8569205 | ||
|
|
b098cb8136 | ||
|
|
75fe7857e4 | ||
|
|
66797aced1 | ||
|
|
4a71e38078 | ||
|
|
9fb07e4fa3 | ||
|
|
74792771ec | ||
|
|
fa6e8a976d | ||
|
|
f20531cff5 | ||
|
|
8b1cbed9ce | ||
|
|
0194e13427 | ||
|
|
82d71abf54 | ||
|
|
e71cfadf6e | ||
|
|
18931c4e98 | ||
|
|
8622c080aa | ||
|
|
cb71b2a593 | ||
|
|
ff5956da41 | ||
|
|
acdafc2efd | ||
|
|
b8874092ad | ||
|
|
ad28ba0b3e | ||
|
|
0d90b60eef | ||
|
|
7c18c9f69c | ||
|
|
975560f46f | ||
|
|
bfe9cbf7d9 | ||
|
|
ccb5eb73db | ||
|
|
d143667bd6 | ||
|
|
87d809abc0 | ||
|
|
4dc5b1a541 | ||
|
|
ddd3e867f9 | ||
|
|
77480d3d69 | ||
|
|
0767c93002 | ||
|
|
e690e6dd3b | ||
|
|
d4665ed768 | ||
|
|
b90a92c0df | ||
|
|
50cfcf9796 | ||
|
|
5d204f09da | ||
|
|
4c0410322f | ||
|
|
fbb2b3f6e7 | ||
|
|
0f09fb49fc | ||
|
|
b0d063d6ed | ||
|
|
a68fe70af4 | ||
|
|
43c7ac281b | ||
|
|
a97ae55a06 | ||
|
|
4a3a6f4186 | ||
|
|
f976724ada | ||
|
|
2632bdaa30 | ||
|
|
91016d7b8c | ||
|
|
2b00e741ca | ||
|
|
d496c11d67 | ||
|
|
5880223517 | ||
|
|
394a5dcd0d | ||
|
|
7365275f46 | ||
|
|
0ecab5fdd4 | ||
|
|
ed0d9f73e4 |
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@v2
|
- uses: actions/checkout@v4.2.2
|
||||||
- name: Publish GH release
|
- name: Publish GH release
|
||||||
uses: softprops/action-gh-release@v0.1.14
|
uses: softprops/action-gh-release@v2.1.0
|
||||||
- name: Build using poetry and publish to PyPi
|
- name: Build using poetry and publish to PyPi
|
||||||
uses: JRubics/poetry-publish@v1.11
|
uses: JRubics/poetry-publish@v2.0
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
|
ci:
|
||||||
|
skip:
|
||||||
|
- unittest
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.5.0
|
rev: v5.0.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: 24.3.0
|
rev: 24.10.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/pycqa/isort
|
||||||
@@ -24,4 +33,3 @@ repos:
|
|||||||
'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
|
||||||
stages: [commit]
|
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
# .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:
|
tools: { python: "3.11" }
|
||||||
python: "3.9"
|
jobs:
|
||||||
|
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
|
|
||||||
@@ -53,6 +53,8 @@ def backend_str(backend: MinerTypes) -> str:
|
|||||||
return "Stock Firmware BitAxe Miners"
|
return "Stock Firmware BitAxe Miners"
|
||||||
case MinerTypes.ICERIVER:
|
case MinerTypes.ICERIVER:
|
||||||
return "Stock Firmware IceRiver Miners"
|
return "Stock Firmware IceRiver Miners"
|
||||||
|
case MinerTypes.HAMMER:
|
||||||
|
return "Stock Firmware Hammer Miners"
|
||||||
|
|
||||||
|
|
||||||
def create_url_str(mtype: str):
|
def create_url_str(mtype: str):
|
||||||
|
|||||||
189
docs/index.md
189
docs/index.md
@@ -11,145 +11,149 @@
|
|||||||
[](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
|
## Installation
|
||||||
|
|
||||||
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include:
|
|
||||||
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default
|
|
||||||
|
|
||||||
```
|
|
||||||
poetry install
|
|
||||||
```
|
|
||||||
|
|
||||||
- [venv](https://docs.python.org/3/library/venv.html): included in Python standard library but has fewer features than other options
|
|
||||||
- [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv): [pyenv](https://github.com/pyenv/pyenv) plugin for managing virtualenvs
|
|
||||||
|
|
||||||
```
|
|
||||||
pyenv install <python version number>
|
|
||||||
pyenv virtualenv <python version number> <env name>
|
|
||||||
pyenv activate <env name>
|
|
||||||
```
|
|
||||||
|
|
||||||
- [conda](https://docs.conda.io/en/latest/)
|
|
||||||
|
|
||||||
##### Installing `pyasic`
|
|
||||||
|
|
||||||
`python -m pip install pyasic` or `poetry install`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system.
|
||||||
|
`pyasic` can be installed directly from pip, either with `pip install pyasic`, or a different command if using a tool like `pypoetry`.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
---
|
---
|
||||||
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.
|
||||||
```python
|
```python3
|
||||||
import asyncio # asyncio for handling the async part
|
import asyncio# (1)!
|
||||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
from pyasic.network import MinerNetwork# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def scan_miners(): # define async scan function to allow awaiting
|
async def scan_miners():# (3)!
|
||||||
# create a miner network
|
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
||||||
# 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
|
|
||||||
|
|
||||||
# scan for miners asynchronously
|
miners = await network.scan()# (5)!
|
||||||
# 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()) # run the scan asynchronously with asyncio.run()
|
asyncio.run(scan_miners())# (6)!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `MinerNetwork` handles the scanning.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Create a miner network.
|
||||||
|
You can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
||||||
|
This uses the 192.168.1.0-255 network.
|
||||||
|
5. Scan for miners asynchronously.
|
||||||
|
This will return the correct type of miners (if they are supported) with all functionality.
|
||||||
|
6. Run the scan asynchronously with asyncio.run().
|
||||||
|
|
||||||
---
|
---
|
||||||
##### Creating miners based on IP
|
### Creating miners based on IP
|
||||||
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner].
|
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 # asyncio for handling the async part
|
import asyncio# (1)!
|
||||||
from pyasic import get_miner # handles miner creation
|
from pyasic import get_miner# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def get_miners(): # define async scan function to allow awaiting
|
async def get_miners():# (3)!
|
||||||
# get the miner with the miner factory
|
miner_1 = await get_miner("192.168.1.75")# (4)!
|
||||||
# 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)
|
miners = await asyncio.gather(*tasks)# (5)!
|
||||||
print(miners)
|
print(miners)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run()
|
asyncio.run(get_miners())# (6)!
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `get_miner` handles the miner type selection.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Get the miner.
|
||||||
|
5. Can also gather these, since they are async.
|
||||||
|
Gathering them will get them both at the same time.
|
||||||
|
This makes it much faster to get a lot of miners at a time.
|
||||||
|
6. Get the miners asynchronously with asyncio.run().
|
||||||
|
|
||||||
## Data gathering
|
## 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
|
import asyncio# (1)!
|
||||||
from pyasic import get_miner
|
from pyasic import get_miner# (2)!
|
||||||
|
|
||||||
async def gather_miner_data():
|
|
||||||
miner = await get_miner("192.168.1.75")
|
async def gather_miner_data():# (3)!
|
||||||
if miner is not None:
|
miner = await get_miner("192.168.1.75")# (4)!
|
||||||
miner_data = await miner.get_data()
|
if miner is not None:# (5)!
|
||||||
print(miner_data) # all data from the dataclass
|
miner_data = await miner.get_data()# (6)!
|
||||||
|
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())
|
asyncio.run(gather_miner_data())# (9)!
|
||||||
```
|
```
|
||||||
---
|
|
||||||
##### Multiple miners
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `get_miner` handles the miner type selection.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Get the miner.
|
||||||
|
5. Make sure the miner exists.
|
||||||
|
If this result is `None`, the miner may be offline.
|
||||||
|
6. Get data from the miner.
|
||||||
|
7. All the data from the dataclass.
|
||||||
|
8. Hashrate of the miner, with unit information.
|
||||||
|
9. Get the miner data asynchronously with asyncio.run().
|
||||||
|
|
||||||
|
### Multiple miners
|
||||||
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
|
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 # asyncio for handling the async part
|
import asyncio# (1)!
|
||||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
from pyasic.network import MinerNetwork# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def gather_miner_data(): # define async scan function to allow awaiting
|
async def gather_miner_data():# (3)!
|
||||||
network = MinerNetwork.from_subnet("192.168.1.50/24")
|
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
||||||
miners = await network.scan()
|
miners = await network.scan()# (5)!
|
||||||
|
|
||||||
# we need to asyncio.gather() all the miners get_data() functions to make them run together
|
|
||||||
all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners])
|
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) # print out all the data one by one
|
print(miner_data)# (7)!
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(gather_miner_data())
|
asyncio.run(gather_miner_data())# (8)!
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `MinerNetwork` handles the scanning.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Create a miner network.
|
||||||
|
5. Scan for miners asynchronously.
|
||||||
|
6. Use `asyncio.gather()` with all the miners' `get_data()` functions to make them run together.
|
||||||
|
7. Print out the data one at a time.
|
||||||
|
8. Get the miner data asynchronously with asyncio.run().
|
||||||
|
|
||||||
## Miner control
|
## 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 control functions defined in [`MinerProtocol`][pyasic.miners.base.MinerProtocol].
|
Every miner class in `pyasic` must implement all the following control functions.
|
||||||
|
|
||||||
These functions are
|
|
||||||
[`check_light`][pyasic.miners.base.MinerProtocol.check_light],
|
[`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],
|
||||||
@@ -166,35 +170,41 @@ These functions are
|
|||||||
[`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
|
import asyncio# (1)!
|
||||||
from pyasic import get_miner
|
from pyasic import get_miner# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def set_fault_light():
|
async def set_fault_light():# (3)!
|
||||||
miner = await get_miner("192.168.1.20")
|
miner = await get_miner("192.168.1.20")# (4)!
|
||||||
|
|
||||||
# call control function
|
await miner.fault_light_on()# (5)!
|
||||||
await miner.fault_light_on()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(set_fault_light())
|
asyncio.run(set_fault_light())# (6)!
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `get_miner` handles the miner type selection.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Get the miner.
|
||||||
|
5. Call the miner control function.
|
||||||
|
6. Call the control function asynchronously with asyncio.run().
|
||||||
|
|
||||||
|
|
||||||
## Helper dataclasses
|
## 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()`](#get-data) function, and is used to have a consistent dataset across all returns.
|
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`][pyasic.miners.base.MinerProtocol.get_data] function, and is used to have a consistent dataset across all returns.
|
||||||
|
|
||||||
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
||||||
|
|
||||||
@@ -213,13 +223,13 @@ average_data = sum(list_of_miner_data, start=MinerData("0.0.0.0"))/len(list_of_m
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
##### [`MinerConfig`][pyasic.config.MinerConfig]
|
### [`MinerConfig`][pyasic.config.MinerConfig]
|
||||||
|
|
||||||
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
||||||
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`](#get-config).
|
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`][pyasic.miners.base.MinerProtocol.get_config].
|
||||||
|
|
||||||
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
||||||
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
In most cases these helper functions should not be used, as [`send_config()`][pyasic.miners.base.MinerProtocol.send_config] takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
||||||
|
|
||||||
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
||||||
```python
|
```python
|
||||||
@@ -241,7 +251,6 @@ 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:
|
||||||
@@ -252,7 +261,7 @@ from pyasic import settings
|
|||||||
settings.update("default_antminer_web_password", "my_pwd")
|
settings.update("default_antminer_web_password", "my_pwd")
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Default values:
|
### Default values:
|
||||||
```
|
```
|
||||||
"network_ping_retries": 1,
|
"network_ping_retries": 1,
|
||||||
"network_ping_timeout": 3,
|
"network_ping_timeout": 3,
|
||||||
|
|||||||
@@ -8,3 +8,10 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## Z15 Pro (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X15.Z15.BMMinerZ15Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -274,6 +274,13 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## S19j Pro (VNish)
|
||||||
|
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## S19a (VNish)
|
## S19a (VNish)
|
||||||
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19a
|
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19a
|
||||||
handler: python
|
handler: python
|
||||||
@@ -295,6 +302,13 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## S19k Pro (VNish)
|
||||||
|
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19kPro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## T19 (VNish)
|
## T19 (VNish)
|
||||||
::: pyasic.miners.antminer.vnish.X19.T19.VNishT19
|
::: pyasic.miners.antminer.vnish.X19.T19.VNishT19
|
||||||
handler: python
|
handler: python
|
||||||
@@ -351,6 +365,20 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## S19j Pro (Hive)
|
||||||
|
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19jPro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## S19 (Hive)
|
||||||
|
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## S19 (LuxOS)
|
## S19 (LuxOS)
|
||||||
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19
|
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19
|
||||||
handler: python
|
handler: python
|
||||||
|
|||||||
@@ -29,6 +29,13 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## T21 (BOS+)
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.T21.BOSMinerT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## S21 (VNish)
|
## S21 (VNish)
|
||||||
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
|
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
|
||||||
handler: python
|
handler: python
|
||||||
|
|||||||
@@ -22,6 +22,20 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## KA3 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X3.KA3.BMMinerKA3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS3 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X3.KS3.BMMinerKS3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## L3+ (VNish)
|
## L3+ (VNish)
|
||||||
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
|
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
|
||||||
handler: python
|
handler: python
|
||||||
|
|||||||
@@ -8,3 +8,10 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS5 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,20 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## K7 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X7.K7.BMMinerK7
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## D7 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## L7 (VNish)
|
## L7 (VNish)
|
||||||
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
|
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
|
||||||
handler: python
|
handler: python
|
||||||
|
|||||||
@@ -8,6 +8,13 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## D9 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## S9 (Stock)
|
## S9 (Stock)
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
@@ -36,6 +43,13 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## L9 (Stock)
|
||||||
|
::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## S9 (BOS+)
|
## S9 (BOS+)
|
||||||
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
|
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
@@ -43,7 +57,7 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
## T9 (Stock)
|
## T9 (Hive)
|
||||||
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
|
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A11X Models
|
## A11X Models
|
||||||
|
|
||||||
|
## Avalon 1126 Pro (Stock)
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.A11X.A1126.CGMinerAvalon1126Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## Avalon 1166 Pro (Stock)
|
## Avalon 1166 Pro (Stock)
|
||||||
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
|
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
|
||||||
handler: python
|
handler: python
|
||||||
|
|||||||
10
docs/miners/avalonminer/nano.md
Normal file
10
docs/miners/avalonminer/nano.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# pyasic
|
||||||
|
## nano Models
|
||||||
|
|
||||||
|
## Avalon Nano 3 (Stock)
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
@@ -10,8 +10,3 @@ 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
|
|
||||||
|
|||||||
@@ -22,3 +22,10 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## Gamma (Stock)
|
||||||
|
::: pyasic.miners.bitaxe.espminer.BM.BM1370.BitAxeGamma
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +1,10 @@
|
|||||||
## Control functionality
|
## Control functionality
|
||||||
|
|
||||||
### Check Light
|
All control functionality is outlined by the [`MinerProtocol`][pyasic.miners.base.MinerProtocol] class.
|
||||||
::: pyasic.miners.base.MinerProtocol.check_light
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Fault Light Off
|
## Miner Protocol
|
||||||
::: pyasic.miners.base.MinerProtocol.fault_light_off
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Fault Light On
|
::: pyasic.miners.base.MinerProtocol
|
||||||
::: 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
|
||||||
|
|||||||
10
docs/miners/hammer/DX.md
Normal file
10
docs/miners/hammer/DX.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# pyasic
|
||||||
|
## DX Models
|
||||||
|
|
||||||
|
## D10 (Stock)
|
||||||
|
::: pyasic.miners.hammer.blackminer.DX.D10.HammerD10
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
@@ -1,6 +1,20 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## KSX Models
|
## KSX Models
|
||||||
|
|
||||||
|
## KS0 (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS0.IceRiverKS0
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS1 (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS1.IceRiverKS1
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## KS2 (Stock)
|
## KS2 (Stock)
|
||||||
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
|
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
|
||||||
handler: python
|
handler: python
|
||||||
@@ -8,3 +22,45 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS3 (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS3L (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3L
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS3M (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3M
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS5 (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS5L (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5L
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## KS5M (Stock)
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5M
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
|||||||
17
docs/miners/innosilicon/A11X.md
Normal file
17
docs/miners/innosilicon/A11X.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# pyasic
|
||||||
|
## A11X Models
|
||||||
|
|
||||||
|
## A11 (Stock)
|
||||||
|
::: pyasic.miners.innosilicon.cgminer.A11X.A11.InnosiliconA11
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## A11MX (Stock)
|
||||||
|
::: pyasic.miners.innosilicon.cgminer.A11X.A11M.InnosiliconA11MX
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
@@ -21,34 +21,42 @@ details {
|
|||||||
<li><a href="../antminer/X3#d3-stock">D3 (Stock)</a></li>
|
<li><a href="../antminer/X3#d3-stock">D3 (Stock)</a></li>
|
||||||
<li><a href="../antminer/X3#hs3-stock">HS3 (Stock)</a></li>
|
<li><a href="../antminer/X3#hs3-stock">HS3 (Stock)</a></li>
|
||||||
<li><a href="../antminer/X3#l3_1-stock">L3+ (Stock)</a></li>
|
<li><a href="../antminer/X3#l3_1-stock">L3+ (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X3#ka3-stock">KA3 (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X3#ks3-stock">KS3 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>X5 Series:</summary>
|
<summary>X5 Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
|
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>X7 Series:</summary>
|
<summary>X7 Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
|
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X7#k7-stock">K7 (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X7#d7-stock">D7 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>X9 Series:</summary>
|
<summary>X9 Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../antminer/X9#e9pro-stock">E9Pro (Stock)</a></li>
|
<li><a href="../antminer/X9#e9pro-stock">E9Pro (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X9#d9-stock">D9 (Stock)</a></li>
|
||||||
<li><a href="../antminer/X9#s9-stock">S9 (Stock)</a></li>
|
<li><a href="../antminer/X9#s9-stock">S9 (Stock)</a></li>
|
||||||
<li><a href="../antminer/X9#s9i-stock">S9i (Stock)</a></li>
|
<li><a href="../antminer/X9#s9i-stock">S9i (Stock)</a></li>
|
||||||
<li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li>
|
<li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li>
|
||||||
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
|
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X9#l9-stock">L9 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>X15 Series:</summary>
|
<summary>X15 Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../antminer/X15#z15-stock">Z15 (Stock)</a></li>
|
<li><a href="../antminer/X15#z15-stock">Z15 (Stock)</a></li>
|
||||||
|
<li><a href="../antminer/X15#z15-pro-stock">Z15 Pro (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
@@ -278,6 +286,7 @@ details {
|
|||||||
<li><a href="../whatsminer/M5X#m50-vh60-stock">M50 VH60 (Stock)</a></li>
|
<li><a href="../whatsminer/M5X#m50-vh60-stock">M50 VH60 (Stock)</a></li>
|
||||||
<li><a href="../whatsminer/M5X#m50-vh70-stock">M50 VH70 (Stock)</a></li>
|
<li><a href="../whatsminer/M5X#m50-vh70-stock">M50 VH70 (Stock)</a></li>
|
||||||
<li><a href="../whatsminer/M5X#m50-vh80-stock">M50 VH80 (Stock)</a></li>
|
<li><a href="../whatsminer/M5X#m50-vh80-stock">M50 VH80 (Stock)</a></li>
|
||||||
|
<li><a href="../whatsminer/M5X#m50-vh90-stock">M50 VH90 (Stock)</a></li>
|
||||||
<li><a href="../whatsminer/M5X#m50-vj10-stock">M50 VJ10 (Stock)</a></li>
|
<li><a href="../whatsminer/M5X#m50-vj10-stock">M50 VJ10 (Stock)</a></li>
|
||||||
<li><a href="../whatsminer/M5X#m50-vj20-stock">M50 VJ20 (Stock)</a></li>
|
<li><a href="../whatsminer/M5X#m50-vj20-stock">M50 VJ20 (Stock)</a></li>
|
||||||
<li><a href="../whatsminer/M5X#m50-vj30-stock">M50 VJ30 (Stock)</a></li>
|
<li><a href="../whatsminer/M5X#m50-vj30-stock">M50 VJ30 (Stock)</a></li>
|
||||||
@@ -369,6 +378,7 @@ details {
|
|||||||
<details>
|
<details>
|
||||||
<summary>A11X Series:</summary>
|
<summary>A11X Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><a href="../avalonminer/A11X#avalon-1126-pro-stock">Avalon 1126 Pro (Stock)</a></li>
|
||||||
<li><a href="../avalonminer/A11X#avalon-1166-pro-stock">Avalon 1166 Pro (Stock)</a></li>
|
<li><a href="../avalonminer/A11X#avalon-1166-pro-stock">Avalon 1166 Pro (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
@@ -378,6 +388,12 @@ details {
|
|||||||
<li><a href="../avalonminer/A12X#avalon-1246-stock">Avalon 1246 (Stock)</a></li>
|
<li><a href="../avalonminer/A12X#avalon-1246-stock">Avalon 1246 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>nano Series:</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
@@ -395,6 +411,13 @@ details {
|
|||||||
<li><a href="../innosilicon/A10X#a10x-stock">A10X (Stock)</a></li>
|
<li><a href="../innosilicon/A10X#a10x-stock">A10X (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>A11X Series:</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../innosilicon/A11X#a11-stock">A11 (Stock)</a></li>
|
||||||
|
<li><a href="../innosilicon/A11X#a11mx-stock">A11MX (Stock)</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
@@ -470,6 +493,7 @@ details {
|
|||||||
<summary>X21 Series:</summary>
|
<summary>X21 Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
|
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
|
||||||
|
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -505,9 +529,11 @@ details {
|
|||||||
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
|
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
|
||||||
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
|
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
|
||||||
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
|
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
|
||||||
|
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
|
||||||
<li><a href="../antminer/X19#s19a-vnish">S19a (VNish)</a></li>
|
<li><a href="../antminer/X19#s19a-vnish">S19a (VNish)</a></li>
|
||||||
<li><a href="../antminer/X19#s19a-pro-vnish">S19a Pro (VNish)</a></li>
|
<li><a href="../antminer/X19#s19a-pro-vnish">S19a Pro (VNish)</a></li>
|
||||||
<li><a href="../antminer/X19#s19-pro-hydro-vnish">S19 Pro Hydro (VNish)</a></li>
|
<li><a href="../antminer/X19#s19-pro-hydro-vnish">S19 Pro Hydro (VNish)</a></li>
|
||||||
|
<li><a href="../antminer/X19#s19k-pro-vnish">S19k Pro (VNish)</a></li>
|
||||||
<li><a href="../antminer/X19#t19-vnish">T19 (VNish)</a></li>
|
<li><a href="../antminer/X19#t19-vnish">T19 (VNish)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
@@ -557,7 +583,14 @@ details {
|
|||||||
<details>
|
<details>
|
||||||
<summary>X9 Series:</summary>
|
<summary>X9 Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
|
<li><a href="../antminer/X9#t9-hive">T9 (Hive)</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>X19 Series:</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../antminer/X19#s19j-pro-hive">S19j Pro (Hive)</a></li>
|
||||||
|
<li><a href="../antminer/X19#s19-hive">S19 (Hive)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -651,6 +684,7 @@ details {
|
|||||||
<li><a href="../bitaxe/BM#supra-stock">Supra (Stock)</a></li>
|
<li><a href="../bitaxe/BM#supra-stock">Supra (Stock)</a></li>
|
||||||
<li><a href="../bitaxe/BM#ultra-stock">Ultra (Stock)</a></li>
|
<li><a href="../bitaxe/BM#ultra-stock">Ultra (Stock)</a></li>
|
||||||
<li><a href="../bitaxe/BM#max-stock">Max (Stock)</a></li>
|
<li><a href="../bitaxe/BM#max-stock">Max (Stock)</a></li>
|
||||||
|
<li><a href="../bitaxe/BM#gamma-stock">Gamma (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -661,7 +695,26 @@ details {
|
|||||||
<details>
|
<details>
|
||||||
<summary>KSX Series:</summary>
|
<summary>KSX Series:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><a href="../iceriver/KSX#ks0-stock">KS0 (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks1-stock">KS1 (Stock)</a></li>
|
||||||
<li><a href="../iceriver/KSX#ks2-stock">KS2 (Stock)</a></li>
|
<li><a href="../iceriver/KSX#ks2-stock">KS2 (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks3-stock">KS3 (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks3l-stock">KS3L (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks3m-stock">KS3M (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks5-stock">KS5 (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks5l-stock">KS5L (Stock)</a></li>
|
||||||
|
<li><a href="../iceriver/KSX#ks5m-stock">KS5M (Stock)</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>Stock Firmware Hammer Miners:</summary>
|
||||||
|
<ul>
|
||||||
|
<details>
|
||||||
|
<summary>DX Series:</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../hammer/DX#d10-stock">D10 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -71,6 +71,13 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## M50 VH90 (Stock)
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH90
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## M50 VJ10 (Stock)
|
## M50 VJ10 (Stock)
|
||||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
|
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
|
||||||
handler: python
|
handler: python
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
jinja2<3.1.4
|
|
||||||
mkdocs
|
|
||||||
mkdocstrings[python]
|
|
||||||
zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
|
||||||
33
mkdocs.yml
33
mkdocs.yml
@@ -1,5 +1,34 @@
|
|||||||
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.inlinehilite
|
||||||
|
- pymdownx.snippets
|
||||||
|
- pymdownx.superfences
|
||||||
nav:
|
nav:
|
||||||
- Introduction: "index.md"
|
- Introduction: "index.md"
|
||||||
- Miners:
|
- Miners:
|
||||||
@@ -50,6 +79,7 @@ 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"
|
||||||
|
- 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"
|
||||||
@@ -62,6 +92,7 @@ nav:
|
|||||||
- Whatsminer M6X: "miners/whatsminer/M6X.md"
|
- Whatsminer M6X: "miners/whatsminer/M6X.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"
|
||||||
@@ -70,6 +101,8 @@ nav:
|
|||||||
- Auradine AT: "miners/auradine/AT.md"
|
- Auradine AT: "miners/auradine/AT.md"
|
||||||
- Blockminer: "miners/blockminer/blockminer.md"
|
- Blockminer: "miners/blockminer/blockminer.md"
|
||||||
- BitAxe BM: "miners/bitaxe/BM.md"
|
- BitAxe BM: "miners/bitaxe/BM.md"
|
||||||
|
- Hammer DX: "miners/hammer/DX.md"
|
||||||
|
- Iceriver KSX: "miners/iceriver/KSX.md"
|
||||||
- Base Miner: "miners/base_miner.md"
|
- Base Miner: "miners/base_miner.md"
|
||||||
- Settings:
|
- Settings:
|
||||||
- Settings: "settings/settings.md"
|
- Settings: "settings/settings.md"
|
||||||
|
|||||||
963
poetry.lock
generated
963
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,25 +13,28 @@
|
|||||||
# 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 pyasic.config.fans import FanModeConfig
|
from pydantic import BaseModel, Field
|
||||||
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.mining.scaling import ScalingConfig
|
||||||
from pyasic.config.pools import PoolConfig
|
from pyasic.config.pools import PoolConfig
|
||||||
from pyasic.config.temperature import TemperatureConfig
|
from pyasic.config.temperature import TemperatureConfig
|
||||||
from pyasic.misc import merge_dicts
|
from pyasic.misc import merge_dicts
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class MinerConfig(BaseModel):
|
||||||
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)
|
||||||
|
fan_mode: FanMode = Field(default_factory=FanModeConfig.default)
|
||||||
|
temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default)
|
||||||
|
mining_mode: MiningMode = Field(default_factory=MiningModeConfig.default)
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
try:
|
try:
|
||||||
@@ -41,7 +44,7 @@ class MinerConfig:
|
|||||||
|
|
||||||
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 asdict(self)
|
return self.model_dump()
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str = 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."""
|
||||||
@@ -107,7 +110,7 @@ class MinerConfig:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str = 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(),
|
||||||
@@ -148,6 +151,30 @@ class MinerConfig:
|
|||||||
**self.pools.as_bitaxe(user_suffix=user_suffix),
|
**self.pools.as_bitaxe(user_suffix=user_suffix),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_luxos(self, user_suffix: str = 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) -> 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."""
|
||||||
@@ -218,13 +245,13 @@ class MinerConfig:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict) -> "MinerConfig":
|
def from_vnish(cls, web_settings: dict, web_presets: list[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),
|
mining_mode=MiningModeConfig.from_vnish(web_settings, web_presets),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -250,3 +277,29 @@ class MinerConfig:
|
|||||||
pools=PoolConfig.from_bitaxe(web_system_info),
|
pools=PoolConfig.from_bitaxe(web_system_info),
|
||||||
fan_mode=FanModeConfig.from_bitaxe(web_system_info),
|
fan_mode=FanModeConfig.from_bitaxe(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
|
||||||
|
) -> "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),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
|
||||||
|
return cls.from_am_modern(*args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
return cls.from_am_modern(web_conf)
|
||||||
|
|||||||
@@ -15,9 +15,10 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
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
|
||||||
@@ -63,6 +64,9 @@ class MinerConfigOption(Enum):
|
|||||||
def as_bitaxe(self) -> dict:
|
def as_bitaxe(self) -> dict:
|
||||||
return self.value.as_bitaxe()
|
return self.value.as_bitaxe()
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return self.value.as_luxos()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
return self.value(*args, **kwargs)
|
return self.value(*args, **kwargs)
|
||||||
|
|
||||||
@@ -77,14 +81,13 @@ class MinerConfigOption(Enum):
|
|||||||
raise KeyError
|
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 asdict(self)
|
return self.model_dump()
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
@@ -125,6 +128,9 @@ class MinerConfigValue:
|
|||||||
def as_bitaxe(self) -> dict:
|
def as_bitaxe(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {}
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
try:
|
try:
|
||||||
return getattr(self, item)
|
return getattr(self, item)
|
||||||
|
|||||||
@@ -15,14 +15,15 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from typing import TypeVar, Union
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
@@ -83,10 +84,24 @@ class FanModeNormal(MinerConfigValue):
|
|||||||
def as_bitaxe(self) -> dict:
|
def as_bitaxe(self) -> dict:
|
||||||
return {"autoFanspeed": 1}
|
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
|
||||||
|
|
||||||
@@ -144,10 +159,24 @@ class FanModeManual(MinerConfigValue):
|
|||||||
def as_bitaxe(self) -> dict:
|
def as_bitaxe(self) -> dict:
|
||||||
return {"autoFanspeed": 0, "fanspeed": self.speed}
|
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":
|
||||||
@@ -167,6 +196,12 @@ class FanModeImmersion(MinerConfigValue):
|
|||||||
def as_mara(self) -> dict:
|
def as_mara(self) -> dict:
|
||||||
return {"general-config": {"environment-profile": "OilImmersionCooling"}}
|
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
|
||||||
@@ -264,7 +299,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(temperature_conf["minimumRequiredFans"])
|
return cls.normal(minimum_fans=temperature_conf["minimumRequiredFans"])
|
||||||
return cls.normal()
|
return cls.normal()
|
||||||
if "manual" in keys:
|
if "manual" in keys:
|
||||||
conf = {}
|
conf = {}
|
||||||
@@ -291,7 +326,9 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
mode = web_config["general-config"]["environment-profile"]
|
mode = web_config["general-config"]["environment-profile"]
|
||||||
if mode == "AirCooling":
|
if mode == "AirCooling":
|
||||||
if web_config["advance-config"]["override-fan-control"]:
|
if web_config["advance-config"]["override-fan-control"]:
|
||||||
return cls.manual(web_config["advance-config"]["fan-fixed-percent"])
|
return cls.manual(
|
||||||
|
speed=web_config["advance-config"]["fan-fixed-percent"]
|
||||||
|
)
|
||||||
return cls.normal()
|
return cls.normal()
|
||||||
return cls.immersion()
|
return cls.immersion()
|
||||||
except LookupError:
|
except LookupError:
|
||||||
@@ -304,3 +341,29 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
return cls.normal()
|
return cls.normal()
|
||||||
else:
|
else:
|
||||||
return cls.manual(speed=web_system_info["fanspeed"])
|
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],
|
||||||
|
)
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import field
|
||||||
|
from typing import TypeVar, Union
|
||||||
|
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
@@ -34,11 +35,11 @@ from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
|||||||
TunerPerformanceMode,
|
TunerPerformanceMode,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .algo import TunerAlgo
|
from .algo import TunerAlgo, TunerAlgoType
|
||||||
|
from .presets import MiningPreset
|
||||||
from .scaling import ScalingConfig
|
from .scaling import ScalingConfig
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeNormal(MinerConfigValue):
|
class MiningModeNormal(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="normal")
|
mode: str = field(init=False, default="normal")
|
||||||
|
|
||||||
@@ -70,8 +71,10 @@ class MiningModeNormal(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"autotunerset": {"enabled": False}}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeSleep(MinerConfigValue):
|
class MiningModeSleep(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="sleep")
|
mode: str = field(init=False, default="sleep")
|
||||||
|
|
||||||
@@ -104,7 +107,6 @@ class MiningModeSleep(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeLPM(MinerConfigValue):
|
class MiningModeLPM(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="low")
|
mode: str = field(init=False, default="low")
|
||||||
|
|
||||||
@@ -127,7 +129,6 @@ class MiningModeLPM(MinerConfigValue):
|
|||||||
return {"settings": {"level": 1}}
|
return {"settings": {"level": 1}}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeHPM(MinerConfigValue):
|
class MiningModeHPM(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="high")
|
mode: str = field(init=False, default="high")
|
||||||
|
|
||||||
@@ -147,12 +148,14 @@ class MiningModeHPM(MinerConfigValue):
|
|||||||
return {"mode": {"mode": "turbo"}}
|
return {"mode": {"mode": "turbo"}}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModePowerTune(MinerConfigValue):
|
class MiningModePowerTune(MinerConfigValue):
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
mode: str = field(init=False, default="power_tuning")
|
mode: str = field(init=False, default="power_tuning")
|
||||||
power: int = None
|
power: int | None = None
|
||||||
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
|
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
|
||||||
scaling: ScalingConfig = None
|
scaling: ScalingConfig | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
|
||||||
@@ -198,7 +201,7 @@ class MiningModePowerTune(MinerConfigValue):
|
|||||||
def as_boser(self) -> dict:
|
def as_boser(self) -> dict:
|
||||||
cfg = {
|
cfg = {
|
||||||
"set_performance_mode": SetPerformanceModeRequest(
|
"set_performance_mode": SetPerformanceModeRequest(
|
||||||
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
save_action=SaveAction.SAVE_AND_APPLY,
|
||||||
mode=PerformanceMode(
|
mode=PerformanceMode(
|
||||||
tuner_mode=TunerPerformanceMode(
|
tuner_mode=TunerPerformanceMode(
|
||||||
power_target=PowerTargetMode(
|
power_target=PowerTargetMode(
|
||||||
@@ -240,13 +243,18 @@ class MiningModePowerTune(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"autotunerset": {"enabled": True}}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeHashrateTune(MinerConfigValue):
|
class MiningModeHashrateTune(MinerConfigValue):
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
mode: str = field(init=False, default="hashrate_tuning")
|
mode: str = field(init=False, default="hashrate_tuning")
|
||||||
hashrate: int = None
|
hashrate: int | None = None
|
||||||
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
|
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
|
||||||
scaling: ScalingConfig = None
|
scaling: ScalingConfig | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
|
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
|
||||||
@@ -275,7 +283,7 @@ class MiningModeHashrateTune(MinerConfigValue):
|
|||||||
def as_boser(self) -> dict:
|
def as_boser(self) -> dict:
|
||||||
cfg = {
|
cfg = {
|
||||||
"set_performance_mode": SetPerformanceModeRequest(
|
"set_performance_mode": SetPerformanceModeRequest(
|
||||||
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
save_action=SaveAction.SAVE_AND_APPLY,
|
||||||
mode=PerformanceMode(
|
mode=PerformanceMode(
|
||||||
tuner_mode=TunerPerformanceMode(
|
tuner_mode=TunerPerformanceMode(
|
||||||
hashrate_target=HashrateTargetMode(
|
hashrate_target=HashrateTargetMode(
|
||||||
@@ -333,8 +341,33 @@ class MiningModeHashrateTune(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ManualBoardSettings(MinerConfigValue):
|
class ManualBoardSettings(MinerConfigValue):
|
||||||
freq: float
|
freq: float
|
||||||
volt: float
|
volt: float
|
||||||
@@ -348,8 +381,10 @@ class ManualBoardSettings(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {"freq": self.freq}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeManual(MinerConfigValue):
|
class MiningModeManual(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="manual")
|
mode: str = field(init=False, default="manual")
|
||||||
|
|
||||||
@@ -370,6 +405,18 @@ class MiningModeManual(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
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
|
@classmethod
|
||||||
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
|
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
|
||||||
# will raise KeyError if it cant find the settings, values cannot be empty
|
# will raise KeyError if it cant find the settings, values cannot be empty
|
||||||
@@ -421,6 +468,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
sleep = MiningModeSleep
|
sleep = MiningModeSleep
|
||||||
power_tuning = MiningModePowerTune
|
power_tuning = MiningModePowerTune
|
||||||
hashrate_tuning = MiningModeHashrateTune
|
hashrate_tuning = MiningModeHashrateTune
|
||||||
|
preset = MiningModePreset
|
||||||
manual = MiningModeManual
|
manual = MiningModeManual
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -512,7 +560,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if autotuning_conf.get("psu_power_limit") is not None:
|
if autotuning_conf.get("psu_power_limit") is not None:
|
||||||
# old autotuning conf
|
# old autotuning conf
|
||||||
return cls.power_tuning(
|
return cls.power_tuning(
|
||||||
autotuning_conf["psu_power_limit"],
|
power=autotuning_conf["psu_power_limit"],
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
||||||
)
|
)
|
||||||
if autotuning_conf.get("mode") is not None:
|
if autotuning_conf.get("mode") is not None:
|
||||||
@@ -521,7 +569,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if mode == "power_target":
|
if mode == "power_target":
|
||||||
if autotuning_conf.get("power_target") is not None:
|
if autotuning_conf.get("power_target") is not None:
|
||||||
return cls.power_tuning(
|
return cls.power_tuning(
|
||||||
autotuning_conf["power_target"],
|
power=autotuning_conf["power_target"],
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
||||||
)
|
)
|
||||||
return cls.power_tuning(
|
return cls.power_tuning(
|
||||||
@@ -530,7 +578,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if mode == "hashrate_target":
|
if mode == "hashrate_target":
|
||||||
if autotuning_conf.get("hashrate_target") is not None:
|
if autotuning_conf.get("hashrate_target") is not None:
|
||||||
return cls.hashrate_tuning(
|
return cls.hashrate_tuning(
|
||||||
autotuning_conf["hashrate_target"],
|
hashrate=autotuning_conf["hashrate_target"],
|
||||||
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
|
||||||
)
|
)
|
||||||
return cls.hashrate_tuning(
|
return cls.hashrate_tuning(
|
||||||
@@ -538,7 +586,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict):
|
def from_vnish(cls, web_settings: dict, web_presets: list[dict]):
|
||||||
try:
|
try:
|
||||||
mode_settings = web_settings["miner"]["overclock"]
|
mode_settings = web_settings["miner"]["overclock"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -547,7 +595,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if mode_settings["preset"] == "disabled":
|
if mode_settings["preset"] == "disabled":
|
||||||
return MiningModeManual.from_vnish(mode_settings)
|
return MiningModeManual.from_vnish(mode_settings)
|
||||||
else:
|
else:
|
||||||
return cls.power_tuning(int(mode_settings["preset"]))
|
return MiningModePreset.from_vnish(mode_settings, web_presets)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_miner_conf: dict):
|
def from_boser(cls, grpc_miner_conf: dict):
|
||||||
@@ -562,7 +610,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if tuner_conf["tunerMode"] == 1:
|
if tuner_conf["tunerMode"] == 1:
|
||||||
if tuner_conf.get("powerTarget") is not None:
|
if tuner_conf.get("powerTarget") is not None:
|
||||||
return cls.power_tuning(
|
return cls.power_tuning(
|
||||||
tuner_conf["powerTarget"]["watt"],
|
power=tuner_conf["powerTarget"]["watt"],
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
||||||
)
|
)
|
||||||
return cls.power_tuning(
|
return cls.power_tuning(
|
||||||
@@ -572,7 +620,7 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if tuner_conf["tunerMode"] == 2:
|
if tuner_conf["tunerMode"] == 2:
|
||||||
if tuner_conf.get("hashrateTarget") is not None:
|
if tuner_conf.get("hashrateTarget") is not None:
|
||||||
return cls.hashrate_tuning(
|
return cls.hashrate_tuning(
|
||||||
int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
||||||
scaling=ScalingConfig.from_boser(
|
scaling=ScalingConfig.from_boser(
|
||||||
grpc_miner_conf, mode="hashrate"
|
grpc_miner_conf, mode="hashrate"
|
||||||
),
|
),
|
||||||
@@ -583,13 +631,13 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
|
|
||||||
if tuner_conf.get("powerTarget") is not None:
|
if tuner_conf.get("powerTarget") is not None:
|
||||||
return cls.power_tuning(
|
return cls.power_tuning(
|
||||||
tuner_conf["powerTarget"]["watt"],
|
power=tuner_conf["powerTarget"]["watt"],
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if tuner_conf.get("hashrateTarget") is not None:
|
if tuner_conf.get("hashrateTarget") is not None:
|
||||||
return cls.hashrate_tuning(
|
return cls.hashrate_tuning(
|
||||||
int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
||||||
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -608,9 +656,9 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
if mode_data.get("Mode") == "turbo":
|
if mode_data.get("Mode") == "turbo":
|
||||||
return cls.high()
|
return cls.high()
|
||||||
if mode_data.get("Ths") is not None:
|
if mode_data.get("Ths") is not None:
|
||||||
return cls.hashrate_tuning(mode_data["Ths"])
|
return cls.hashrate_tuning(hashrate=mode_data["Ths"])
|
||||||
if mode_data.get("Power") is not None:
|
if mode_data.get("Power") is not None:
|
||||||
return cls.power_tuning(mode_data["Power"])
|
return cls.power_tuning(power=mode_data["Power"])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
@@ -638,3 +686,18 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
except LookupError:
|
except LookupError:
|
||||||
pass
|
pass
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
|
|
||||||
|
MiningMode = TypeVar(
|
||||||
|
"MiningMode",
|
||||||
|
bound=Union[
|
||||||
|
MiningModeNormal,
|
||||||
|
MiningModeHPM,
|
||||||
|
MiningModeLPM,
|
||||||
|
MiningModeSleep,
|
||||||
|
MiningModeManual,
|
||||||
|
MiningModePowerTune,
|
||||||
|
MiningModeHashrateTune,
|
||||||
|
MiningModePreset,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from typing import TypeVar, Union
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class StandardTuneAlgo(MinerConfigValue):
|
class StandardTuneAlgo(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="standard")
|
mode: str = field(init=False, default="standard")
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@ class StandardTuneAlgo(MinerConfigValue):
|
|||||||
return VOptAlgo().as_epic()
|
return VOptAlgo().as_epic()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class VOptAlgo(MinerConfigValue):
|
class VOptAlgo(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="voltage_optimizer")
|
mode: str = field(init=False, default="voltage_optimizer")
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ class VOptAlgo(MinerConfigValue):
|
|||||||
return "VoltageOptimizer"
|
return "VoltageOptimizer"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BoardTuneAlgo(MinerConfigValue):
|
class BoardTuneAlgo(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="board_tune")
|
mode: str = field(init=False, default="board_tune")
|
||||||
|
|
||||||
@@ -29,7 +27,6 @@ class BoardTuneAlgo(MinerConfigValue):
|
|||||||
return "BoardTune"
|
return "BoardTune"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ChipTuneAlgo(MinerConfigValue):
|
class ChipTuneAlgo(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="chip_tune")
|
mode: str = field(init=False, default="chip_tune")
|
||||||
|
|
||||||
@@ -37,7 +34,6 @@ class ChipTuneAlgo(MinerConfigValue):
|
|||||||
return "ChipTune"
|
return "ChipTune"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TunerAlgo(MinerConfigOption):
|
class TunerAlgo(MinerConfigOption):
|
||||||
standard = StandardTuneAlgo
|
standard = StandardTuneAlgo
|
||||||
voltage_optimizer = VOptAlgo
|
voltage_optimizer = VOptAlgo
|
||||||
@@ -45,11 +41,11 @@ class TunerAlgo(MinerConfigOption):
|
|||||||
chip_tune = ChipTuneAlgo
|
chip_tune = ChipTuneAlgo
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls):
|
def default(cls) -> TunerAlgoType:
|
||||||
return cls.standard()
|
return cls.standard()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict | None):
|
def from_dict(cls, dict_conf: dict | None) -> TunerAlgoType:
|
||||||
mode = dict_conf.get("mode")
|
mode = dict_conf.get("mode")
|
||||||
if mode is None:
|
if mode is None:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
@@ -57,3 +53,14 @@ class TunerAlgo(MinerConfigOption):
|
|||||||
cls_attr = getattr(cls, mode)
|
cls_attr = getattr(cls, mode)
|
||||||
if cls_attr is not None:
|
if cls_attr is not None:
|
||||||
return cls_attr().from_dict(dict_conf)
|
return cls_attr().from_dict(dict_conf)
|
||||||
|
|
||||||
|
|
||||||
|
TunerAlgoType = TypeVar(
|
||||||
|
"TunerAlgoType",
|
||||||
|
bound=Union[
|
||||||
|
StandardTuneAlgo,
|
||||||
|
VOptAlgo,
|
||||||
|
BoardTuneAlgo,
|
||||||
|
ChipTuneAlgo,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
34
pyasic/config/mining/presets.py
Normal file
34
pyasic/config/mining/presets.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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 = False
|
||||||
|
|
||||||
|
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(" 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,
|
||||||
|
)
|
||||||
@@ -20,7 +20,6 @@ from dataclasses import dataclass
|
|||||||
from pyasic.config.base import MinerConfigValue
|
from pyasic.config.base import MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ScalingShutdown(MinerConfigValue):
|
class ScalingShutdown(MinerConfigValue):
|
||||||
enabled: bool = False
|
enabled: bool = False
|
||||||
duration: int = None
|
duration: int = None
|
||||||
@@ -35,7 +34,9 @@ class ScalingShutdown(MinerConfigValue):
|
|||||||
def from_bosminer(cls, power_scaling_conf: dict):
|
def from_bosminer(cls, power_scaling_conf: dict):
|
||||||
sd_enabled = power_scaling_conf.get("shutdown_enabled")
|
sd_enabled = power_scaling_conf.get("shutdown_enabled")
|
||||||
if sd_enabled is not None:
|
if sd_enabled is not None:
|
||||||
return cls(sd_enabled, power_scaling_conf.get("shutdown_duration"))
|
return cls(
|
||||||
|
enabled=sd_enabled, duration=power_scaling_conf.get("shutdown_duration")
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -43,9 +44,12 @@ class ScalingShutdown(MinerConfigValue):
|
|||||||
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
||||||
if sd_enabled is not None:
|
if sd_enabled is not None:
|
||||||
try:
|
try:
|
||||||
return cls(sd_enabled, power_scaling_conf["shutdownDuration"]["hours"])
|
return cls(
|
||||||
|
enabled=sd_enabled,
|
||||||
|
duration=power_scaling_conf["shutdownDuration"]["hours"],
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return cls(sd_enabled)
|
return cls(enabled=sd_enabled)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
@@ -60,7 +64,6 @@ class ScalingShutdown(MinerConfigValue):
|
|||||||
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
|
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ScalingConfig(MinerConfigValue):
|
class ScalingConfig(MinerConfigValue):
|
||||||
step: int = None
|
step: int = None
|
||||||
minimum: int = None
|
minimum: int = None
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ 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 (
|
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
||||||
PoolConfiguration,
|
PoolConfiguration,
|
||||||
@@ -30,122 +31,99 @@ from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@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) -> dict:
|
def as_am_modern(self, user_suffix: str | None = 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_wm(self, idx: int = 1, user_suffix: str = None) -> dict:
|
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
|
||||||
return {
|
|
||||||
f"pool_{idx}": self.url,
|
|
||||||
f"worker_{idx}": f"{self.user}{user_suffix}",
|
|
||||||
f"passwd_{idx}": self.password,
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
f"pool_{idx}": self.url,
|
f"pool_{idx}": self.url,
|
||||||
f"worker_{idx}": self.user,
|
f"worker_{idx}": f"{self.user}{user_suffix or ''}",
|
||||||
f"passwd_{idx}": self.password,
|
f"passwd_{idx}": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_am_old(self, idx: int = 1, user_suffix: str = None) -> dict:
|
def as_am_old(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
|
||||||
return {
|
|
||||||
f"_ant_pool{idx}url": self.url,
|
|
||||||
f"_ant_pool{idx}user": f"{self.user}{user_suffix}",
|
|
||||||
f"_ant_pool{idx}pw": self.password,
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
f"_ant_pool{idx}url": self.url,
|
f"_ant_pool{idx}url": self.url,
|
||||||
f"_ant_pool{idx}user": self.user,
|
f"_ant_pool{idx}user": f"{self.user}{user_suffix or ''}",
|
||||||
f"_ant_pool{idx}pw": self.password,
|
f"_ant_pool{idx}pw": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str = None) -> dict:
|
def as_goldshell(self, user_suffix: str | None = 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) -> str:
|
def as_avalon(self, user_suffix: str | None = None) -> str:
|
||||||
if user_suffix is not None:
|
return ",".join([self.url, f"{self.user}{user_suffix or ''}", self.password])
|
||||||
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) -> dict:
|
def as_inno(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
|
||||||
return {
|
|
||||||
f"Pool{idx}": self.url,
|
|
||||||
f"UserName{idx}": f"{self.user}{user_suffix}",
|
|
||||||
f"Password{idx}": self.password,
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
f"Pool{idx}": self.url,
|
f"Pool{idx}": self.url,
|
||||||
f"UserName{idx}": self.user,
|
f"UserName{idx}": f"{self.user}{user_suffix or ''}",
|
||||||
f"Password{idx}": self.password,
|
f"Password{idx}": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str = None) -> dict:
|
def as_bosminer(self, user_suffix: str | None = 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) -> dict:
|
def as_auradine(self, user_suffix: str | None = 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):
|
def as_epic(self, user_suffix: str | None = 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) -> dict:
|
def as_mara(self, user_suffix: str | None = 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_bitaxe(self, user_suffix: str = None) -> dict:
|
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||||
return {
|
return {
|
||||||
"stratumURL": self.url,
|
"stratumURL": self.url,
|
||||||
"stratumUser": f"{self.user}{user_suffix}",
|
"stratumUser": f"{self.user}{user_suffix or ''}",
|
||||||
"stratumPassword": self.password,
|
"stratumPassword": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_boser(self) -> PoolConfiguration:
|
def as_boser(self, user_suffix: str | None = None) -> PoolConfiguration:
|
||||||
return PoolConfiguration(
|
return PoolConfiguration(
|
||||||
url=self.url, user=self.user, password=self.password, enabled=True
|
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":
|
||||||
return cls(
|
return cls(
|
||||||
@@ -192,7 +170,7 @@ class Pool(MinerConfigValue):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_pool: dict) -> "Pool":
|
def from_vnish(cls, web_pool: dict) -> "Pool":
|
||||||
return cls(
|
return cls(
|
||||||
url=web_pool["url"],
|
url="stratum+tcp://" + web_pool["url"],
|
||||||
user=web_pool["user"],
|
user=web_pool["user"],
|
||||||
password=web_pool["pass"],
|
password=web_pool["pass"],
|
||||||
)
|
)
|
||||||
@@ -222,12 +200,23 @@ class Pool(MinerConfigValue):
|
|||||||
password=web_system_info.get("stratumPassword", ""),
|
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
|
name: str | None = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
@@ -235,18 +224,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) -> list:
|
def as_am_modern(self, user_suffix: str | None = 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("", "", "").as_am_modern())
|
pools.append(Pool(url="", user="", password="").as_am_modern())
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> dict:
|
def as_wm(self, user_suffix: str | None = None) -> dict:
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
@@ -255,11 +244,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("", "", "").as_wm(idx=idx + 1))
|
pools.update(**Pool(url="", user="", password="").as_wm(idx=idx + 1))
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str = None) -> dict:
|
def as_am_old(self, user_suffix: str | None = None) -> dict:
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
@@ -268,19 +257,21 @@ 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(**Pool("", "", "").as_am_old(idx=idx + 1))
|
pools.update(
|
||||||
|
**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) -> list:
|
def as_goldshell(self, user_suffix: str | None = 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) -> str:
|
def as_avalon(self, user_suffix: str | None = 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("", "", "").as_avalon()
|
return Pool(url="", user="", password="").as_avalon()
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str = None) -> dict:
|
def as_inno(self, user_suffix: str | None = None) -> dict:
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
@@ -289,11 +280,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("", "", "").as_inno(idx=idx + 1))
|
pools.update(**Pool(url="", user="", password="").as_inno(idx=idx + 1))
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str = None) -> dict:
|
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.pools) > 0:
|
if len(self.pools) > 0:
|
||||||
conf = {
|
conf = {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
@@ -306,25 +297,28 @@ class PoolGroup(MinerConfigValue):
|
|||||||
return conf
|
return conf
|
||||||
return {"name": "Group", "pool": []}
|
return {"name": "Group", "pool": []}
|
||||||
|
|
||||||
def as_auradine(self, user_suffix: str = None) -> list:
|
def as_auradine(self, user_suffix: str | None = 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) -> list:
|
def as_epic(self, user_suffix: str | None = None) -> list:
|
||||||
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) -> list:
|
def as_mara(self, user_suffix: str | None = None) -> list:
|
||||||
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
|
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
|
||||||
|
|
||||||
def as_bitaxe(self, user_suffix: str = None) -> dict:
|
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||||
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
|
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str = None) -> PoolGroupConfiguration:
|
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
|
||||||
return PoolGroupConfiguration(
|
return PoolGroupConfiguration(
|
||||||
name=self.name,
|
name=self.name,
|
||||||
quota=Quota(value=self.quota),
|
quota=Quota(value=self.quota),
|
||||||
pools=[p.as_boser() for p in self.pools],
|
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 = {}
|
||||||
@@ -359,11 +353,11 @@ class PoolGroup(MinerConfigValue):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
||||||
return cls([Pool.from_goldshell(p) for p in web_pools])
|
return cls(pools=[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([Pool.from_inno(p) for p in web_pools])
|
return cls(pools=[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":
|
||||||
@@ -377,7 +371,9 @@ class PoolGroup(MinerConfigValue):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
|
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
|
||||||
return cls([Pool.from_vnish(p) for p in web_settings_pools])
|
return cls(
|
||||||
|
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":
|
||||||
@@ -402,10 +398,18 @@ class PoolGroup(MinerConfigValue):
|
|||||||
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
|
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
|
||||||
return cls(pools=[Pool.from_bitaxe(web_system_info)])
|
return cls(pools=[Pool.from_bitaxe(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":
|
||||||
@@ -427,52 +431,52 @@ 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) -> dict:
|
def as_am_modern(self, user_suffix: str | None = 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_wm(self, user_suffix: str = None) -> dict:
|
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) -> dict:
|
def as_am_old(self, user_suffix: str | None = 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) -> dict:
|
def as_goldshell(self, user_suffix: str | None = 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) -> dict:
|
def as_avalon(self, user_suffix: str | None = 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) -> dict:
|
def as_inno(self, user_suffix: str | None = 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) -> dict:
|
def as_bosminer(self, user_suffix: str | None = 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) -> dict:
|
def as_boser(self, user_suffix: str | None = None) -> dict:
|
||||||
return {
|
return {
|
||||||
"set_pool_groups": SetPoolGroupsRequest(
|
"set_pool_groups": SetPoolGroupsRequest(
|
||||||
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
save_action=SaveAction.SAVE_AND_APPLY,
|
||||||
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
|
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_auradine(self, user_suffix: str = None) -> dict:
|
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {
|
return {
|
||||||
"updatepools": {
|
"updatepools": {
|
||||||
@@ -481,7 +485,7 @@ class PoolConfig(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
return {"updatepools": {"pools": PoolGroup().as_auradine()}}
|
return {"updatepools": {"pools": PoolGroup().as_auradine()}}
|
||||||
|
|
||||||
def as_epic(self, user_suffix: str = None) -> dict:
|
def as_epic(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {
|
return {
|
||||||
"pools": {
|
"pools": {
|
||||||
@@ -498,14 +502,20 @@ class PoolConfig(MinerConfigValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_mara(self, user_suffix: str = None) -> dict:
|
def as_mara(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
|
||||||
return {"pools": []}
|
return {"pools": []}
|
||||||
|
|
||||||
def as_bitaxe(self, user_suffix: str = None) -> dict:
|
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||||
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
|
return self.groups[0].as_bitaxe(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:
|
||||||
@@ -514,38 +524,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([PoolGroup.from_api(pool_data)])
|
return cls(groups=[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([PoolGroup.from_epic(pool_data)])
|
return cls(groups=[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":
|
||||||
pool_data = web_conf["pools"]
|
pool_data = web_conf["pools"]
|
||||||
|
|
||||||
return cls([PoolGroup.from_am_modern(pool_data)])
|
return cls(groups=[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([PoolGroup.from_goldshell(web_pools)])
|
return cls(groups=[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([PoolGroup.from_inno(web_pools)])
|
return cls(groups=[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([PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
|
return cls(groups=[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([PoolGroup.from_vnish(web_settings["miner"]["pools"])])
|
return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@@ -568,3 +578,24 @@ class PoolConfig(MinerConfigValue):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
|
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
|
||||||
return cls(groups=[PoolGroup.from_bitaxe(web_system_info)])
|
return cls(groups=[PoolGroup.from_bitaxe(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"]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ 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
|
target: int | None = None
|
||||||
hot: int = None
|
hot: int | None = None
|
||||||
danger: int = None
|
danger: int | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls):
|
def default(cls):
|
||||||
@@ -54,6 +53,12 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
temps_config["temps"]["shutdown"] = self.hot
|
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(
|
||||||
@@ -93,9 +98,16 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
|
|
||||||
@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(target=web_settings["miner"]["cooling"]["mode"]["param"])
|
return cls(
|
||||||
|
target=web_settings["miner"]["cooling"]["mode"]["param"],
|
||||||
|
danger=dangerous_temp,
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return cls()
|
return cls()
|
||||||
@@ -130,3 +142,16 @@ 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,27 +13,27 @@
|
|||||||
# 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 copy
|
||||||
import json
|
|
||||||
import time
|
import time
|
||||||
from dataclasses import asdict, dataclass, field, fields
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any, List, Union
|
from typing import Any, List, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field, computed_field
|
||||||
|
|
||||||
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 pyasic.device.algorithm.hashrate.base import GenericHashrate
|
||||||
|
|
||||||
|
from ..device.algorithm.hashrate.unit.base import GenericUnit
|
||||||
from .boards import HashBoard
|
from .boards import HashBoard
|
||||||
from .device import DeviceInfo
|
from .device import DeviceInfo
|
||||||
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
||||||
from .fans import Fan
|
from .fans import Fan
|
||||||
from .hashrate import AlgoHashRate, HashUnit
|
|
||||||
from pyasic.data.pools import PoolMetrics
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class MinerData(BaseModel):
|
||||||
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:
|
||||||
@@ -50,7 +50,6 @@ class MinerData:
|
|||||||
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.
|
||||||
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.
|
||||||
@@ -77,58 +76,44 @@ class MinerData:
|
|||||||
|
|
||||||
# general
|
# general
|
||||||
ip: str
|
ip: str
|
||||||
_datetime: datetime = field(repr=False, default=None)
|
raw_datetime: datetime = Field(
|
||||||
datetime: str = field(init=False)
|
exclude=True, default_factory=datetime.now(timezone.utc).astimezone, repr=False
|
||||||
timestamp: int = field(init=False)
|
)
|
||||||
|
|
||||||
# about
|
# about
|
||||||
device_info: DeviceInfo = None
|
device_info: DeviceInfo | None = None
|
||||||
make: str = field(init=False)
|
mac: str | None = None
|
||||||
model: str = field(init=False)
|
api_ver: str | None = None
|
||||||
firmware: str = field(init=False)
|
fw_ver: str | None = None
|
||||||
algo: str = field(init=False)
|
hostname: str | None = None
|
||||||
mac: str = None
|
|
||||||
api_ver: str = None
|
|
||||||
fw_ver: str = None
|
|
||||||
hostname: str = None
|
|
||||||
|
|
||||||
# hashrate
|
# hashrate
|
||||||
hashrate: AlgoHashRate = field(init=False)
|
raw_hashrate: AlgoHashRateType = Field(exclude=True, default=None, repr=False)
|
||||||
_hashrate: AlgoHashRate = field(repr=False, default=None)
|
|
||||||
|
|
||||||
# expected
|
# expected
|
||||||
expected_hashrate: float = None
|
expected_hashrate: AlgoHashRateType | None = None
|
||||||
expected_hashboards: int = None
|
expected_hashboards: int | None = None
|
||||||
expected_chips: int = None
|
expected_chips: int | None = None
|
||||||
expected_fans: int = None
|
expected_fans: int | None = None
|
||||||
|
|
||||||
# % expected
|
|
||||||
percent_expected_chips: float = field(init=False)
|
|
||||||
percent_expected_hashrate: float = field(init=False)
|
|
||||||
percent_expected_wattage: float = field(init=False)
|
|
||||||
|
|
||||||
# temperature
|
# temperature
|
||||||
temperature_avg: int = field(init=False)
|
env_temp: float | None = None
|
||||||
env_temp: float = None
|
|
||||||
|
|
||||||
# power
|
# power
|
||||||
wattage: int = None
|
wattage: int | None = None
|
||||||
wattage_limit: int = field(init=False)
|
voltage: float | None = None
|
||||||
voltage: float = None
|
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False)
|
||||||
_wattage_limit: int = field(repr=False, default=None)
|
|
||||||
|
|
||||||
# fans
|
# fans
|
||||||
fans: List[Fan] = field(default_factory=list)
|
fans: List[Fan] = Field(default_factory=list)
|
||||||
fan_psu: int = None
|
fan_psu: int | None = None
|
||||||
|
|
||||||
# boards
|
# boards
|
||||||
hashboards: List[HashBoard] = field(default_factory=list)
|
hashboards: List[HashBoard] = Field(default_factory=list)
|
||||||
total_chips: int = field(init=False)
|
|
||||||
nominal: bool = field(init=False)
|
|
||||||
|
|
||||||
# config
|
# config
|
||||||
config: MinerConfig = None
|
config: MinerConfig | None = None
|
||||||
fault_light: Union[bool, None] = None
|
fault_light: bool | None = None
|
||||||
|
|
||||||
# errors
|
# errors
|
||||||
errors: List[
|
errors: List[
|
||||||
@@ -138,26 +123,18 @@ class MinerData:
|
|||||||
X19Error,
|
X19Error,
|
||||||
InnosiliconError,
|
InnosiliconError,
|
||||||
]
|
]
|
||||||
] = field(default_factory=list)
|
] = Field(default_factory=list)
|
||||||
|
|
||||||
# mining state
|
# mining state
|
||||||
is_mining: bool = True
|
is_mining: bool = True
|
||||||
uptime: int = None
|
uptime: int | None = None
|
||||||
efficiency: int = field(init=False)
|
|
||||||
|
|
||||||
# pools
|
# pools
|
||||||
pools: list[PoolMetrics] = field(default_factory=list)
|
pools: list[PoolMetrics] = Field(default_factory=list)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls):
|
def fields(cls):
|
||||||
return [f.name for f in fields(cls) if not f.name.startswith("_")]
|
return list(cls.model_fields.keys())
|
||||||
|
|
||||||
@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:
|
||||||
@@ -185,19 +162,19 @@ class MinerData:
|
|||||||
|
|
||||||
def __floordiv__(self, other):
|
def __floordiv__(self, other):
|
||||||
cp = copy.deepcopy(self)
|
cp = copy.deepcopy(self)
|
||||||
for key in self:
|
for key in self.fields():
|
||||||
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, round(item / other, 2))
|
setattr(cp, key, item / other)
|
||||||
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:
|
for key in self.fields():
|
||||||
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:
|
||||||
@@ -217,34 +194,37 @@ class MinerData:
|
|||||||
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): # noqa - Skip PyCharm inspection
|
def hashrate(self) -> AlgoHashRateType | None:
|
||||||
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=type(hr_data[0])(0))
|
return sum(hr_data, start=self.hashboards[0].hashrate.__class__(rate=0))
|
||||||
return self._hashrate
|
return self.raw_hashrate
|
||||||
|
|
||||||
@hashrate.setter
|
@hashrate.setter
|
||||||
def hashrate(self, val):
|
def hashrate(self, val):
|
||||||
self._hashrate = val
|
self.raw_hashrate = val
|
||||||
|
|
||||||
|
@computed_field # type: ignore[misc]
|
||||||
@property
|
@property
|
||||||
def wattage_limit(self): # noqa - Skip PyCharm inspection
|
def wattage_limit(self) -> int | None:
|
||||||
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._wattage_limit
|
return self.raw_wattage_limit
|
||||||
|
|
||||||
@wattage_limit.setter
|
@wattage_limit.setter
|
||||||
def wattage_limit(self, val: int):
|
def wattage_limit(self, val: int):
|
||||||
self._wattage_limit = val
|
self.raw_wattage_limit = val
|
||||||
|
|
||||||
|
@computed_field # type: ignore[misc]
|
||||||
@property
|
@property
|
||||||
def total_chips(self): # noqa - Skip PyCharm inspection
|
def total_chips(self) -> int | None:
|
||||||
if len(self.hashboards) > 0:
|
if len(self.hashboards) > 0:
|
||||||
chip_data = []
|
chip_data = []
|
||||||
for item in self.hashboards:
|
for item in self.hashboards:
|
||||||
@@ -254,34 +234,25 @@ class MinerData:
|
|||||||
return sum(chip_data)
|
return sum(chip_data)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@total_chips.setter
|
@computed_field # type: ignore[misc]
|
||||||
def total_chips(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nominal(self): # noqa - Skip PyCharm inspection
|
def nominal(self) -> bool | None:
|
||||||
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
|
||||||
|
|
||||||
@nominal.setter
|
@computed_field # type: ignore[misc]
|
||||||
def nominal(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percent_expected_chips(self): # noqa - Skip PyCharm inspection
|
def percent_expected_chips(self) -> int | None:
|
||||||
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)
|
||||||
|
|
||||||
@percent_expected_chips.setter
|
@computed_field # type: ignore[misc]
|
||||||
def percent_expected_chips(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percent_expected_hashrate(self): # noqa - Skip PyCharm inspection
|
def percent_expected_hashrate(self) -> int | None:
|
||||||
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:
|
try:
|
||||||
@@ -289,12 +260,9 @@ class MinerData:
|
|||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@percent_expected_hashrate.setter
|
@computed_field # type: ignore[misc]
|
||||||
def percent_expected_hashrate(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percent_expected_wattage(self): # noqa - Skip PyCharm inspection
|
def percent_expected_wattage(self) -> int | None:
|
||||||
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:
|
try:
|
||||||
@@ -302,12 +270,9 @@ class MinerData:
|
|||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@percent_expected_wattage.setter
|
@computed_field # type: ignore[misc]
|
||||||
def percent_expected_wattage(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def temperature_avg(self): # noqa - Skip PyCharm inspection
|
def temperature_avg(self) -> int | None:
|
||||||
total_temp = 0
|
total_temp = 0
|
||||||
temp_count = 0
|
temp_count = 0
|
||||||
for hb in self.hashboards:
|
for hb in self.hashboards:
|
||||||
@@ -318,12 +283,9 @@ class MinerData:
|
|||||||
return None
|
return None
|
||||||
return round(total_temp / temp_count)
|
return round(total_temp / temp_count)
|
||||||
|
|
||||||
@temperature_avg.setter
|
@computed_field # type: ignore[misc]
|
||||||
def temperature_avg(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def efficiency(self): # noqa - Skip PyCharm inspection
|
def efficiency(self) -> int | None:
|
||||||
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:
|
try:
|
||||||
@@ -331,67 +293,45 @@ class MinerData:
|
|||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@efficiency.setter
|
@computed_field # type: ignore[misc]
|
||||||
def efficiency(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def datetime(self): # noqa - Skip PyCharm inspection
|
def datetime(self) -> str:
|
||||||
return self._datetime.isoformat()
|
return self.raw_datetime.isoformat()
|
||||||
|
|
||||||
@datetime.setter
|
|
||||||
def datetime(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[misc]
|
||||||
@property
|
@property
|
||||||
def timestamp(self): # noqa - Skip PyCharm inspection
|
def timestamp(self) -> int:
|
||||||
return int(time.mktime(self._datetime.timetuple()))
|
return int(time.mktime(self.raw_datetime.timetuple()))
|
||||||
|
|
||||||
@timestamp.setter
|
|
||||||
def timestamp(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[misc]
|
||||||
@property
|
@property
|
||||||
def make(self): # noqa - Skip PyCharm inspection
|
def make(self) -> str:
|
||||||
if self.device_info.make is not None:
|
if self.device_info.make is not None:
|
||||||
return str(self.device_info.make)
|
return str(self.device_info.make)
|
||||||
|
|
||||||
@make.setter
|
@computed_field # type: ignore[misc]
|
||||||
def make(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self): # noqa - Skip PyCharm inspection
|
def model(self) -> str:
|
||||||
if self.device_info.model is not None:
|
if self.device_info.model is not None:
|
||||||
return str(self.device_info.model)
|
return str(self.device_info.model)
|
||||||
|
|
||||||
@model.setter
|
@computed_field # type: ignore[misc]
|
||||||
def model(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def firmware(self): # noqa - Skip PyCharm inspection
|
def firmware(self) -> str:
|
||||||
if self.device_info.firmware is not None:
|
if self.device_info.firmware is not None:
|
||||||
return str(self.device_info.firmware)
|
return str(self.device_info.firmware)
|
||||||
|
|
||||||
@firmware.setter
|
@computed_field # type: ignore[misc]
|
||||||
def firmware(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def algo(self): # noqa - Skip PyCharm inspection
|
def algo(self) -> str:
|
||||||
if self.device_info.algo is not None:
|
if self.device_info.algo is not None:
|
||||||
return str(self.device_info.algo)
|
return str(self.device_info.algo)
|
||||||
|
|
||||||
@algo.setter
|
|
||||||
def algo(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def keys(self) -> list:
|
def keys(self) -> list:
|
||||||
return [f.name for f in fields(self)]
|
return list(self.model_fields.keys())
|
||||||
|
|
||||||
def asdict(self) -> dict:
|
def asdict(self) -> dict:
|
||||||
return asdict(self, dict_factory=self.dict_factory)
|
return self.model_dump()
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
"""Get this dataclass as a dictionary.
|
"""Get this dataclass as a dictionary.
|
||||||
@@ -407,7 +347,7 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A JSON version of this class.
|
A JSON version of this class.
|
||||||
"""
|
"""
|
||||||
return json.dumps(self.as_dict())
|
return self.model_dump_json()
|
||||||
|
|
||||||
def as_csv(self) -> str:
|
def as_csv(self) -> str:
|
||||||
"""Get this dataclass as CSV.
|
"""Get this dataclass as CSV.
|
||||||
@@ -436,7 +376,7 @@ class MinerData:
|
|||||||
field_data = []
|
field_data = []
|
||||||
|
|
||||||
tags = ["ip", "mac", "model", "hostname"]
|
tags = ["ip", "mac", "model", "hostname"]
|
||||||
for attribute in self:
|
for attribute in self.fields():
|
||||||
if attribute in tags:
|
if attribute in tags:
|
||||||
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
|
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
|
||||||
tag_data.append(f"{attribute}={escaped_data}")
|
tag_data.append(f"{attribute}={escaped_data}")
|
||||||
|
|||||||
@@ -13,15 +13,16 @@
|
|||||||
# 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 .hashrate import AlgoHashRate
|
from pydantic import BaseModel, field_serializer
|
||||||
|
|
||||||
|
from pyasic.device.algorithm.hashrate import AlgoHashRateType
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class HashBoard(BaseModel):
|
||||||
class HashBoard:
|
|
||||||
"""A Dataclass to standardize hashboard data.
|
"""A Dataclass to standardize hashboard data.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -39,16 +40,16 @@ class HashBoard:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
slot: int = 0
|
slot: int = 0
|
||||||
hashrate: AlgoHashRate = None
|
hashrate: AlgoHashRateType | None = None
|
||||||
temp: int = None
|
temp: float | None = None
|
||||||
chip_temp: int = None
|
chip_temp: float | None = None
|
||||||
chips: int = None
|
chips: int | None = None
|
||||||
expected_chips: int = None
|
expected_chips: int | None = None
|
||||||
serial_number: str = None
|
serial_number: str | None = None
|
||||||
missing: bool = True
|
missing: bool = True
|
||||||
tuned: bool = None
|
tuned: bool | None = None
|
||||||
active: bool = None
|
active: bool | None = None
|
||||||
voltage: float = None
|
voltage: float | None = None
|
||||||
|
|
||||||
def get(self, __key: str, default: Any = None):
|
def get(self, __key: str, default: Any = None):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,14 +1,31 @@
|
|||||||
from dataclasses import dataclass
|
from pydantic import BaseModel, ConfigDict, field_serializer
|
||||||
|
|
||||||
from pyasic.device.algorithm import MinerAlgo
|
from pyasic.device.algorithm import MinerAlgoType
|
||||||
from pyasic.device.firmware import MinerFirmware
|
from pyasic.device.firmware import MinerFirmware
|
||||||
from pyasic.device.makes import MinerMake
|
from pyasic.device.makes import MinerMake
|
||||||
from pyasic.device.models import MinerModel
|
from pyasic.device.models import MinerModelType
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class DeviceInfo(BaseModel):
|
||||||
class DeviceInfo:
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
make: MinerMake = None
|
|
||||||
model: MinerModel = None
|
make: MinerMake | None = None
|
||||||
firmware: MinerFirmware = None
|
model: MinerModelType | None = None
|
||||||
algo: MinerAlgo = 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,12 +13,10 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class X19Error(BaseMinerError):
|
||||||
class X19Error:
|
|
||||||
"""A Dataclass to handle error codes of X19 miners.
|
"""A Dataclass to handle error codes of X19 miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -28,10 +26,3 @@ class X19Error:
|
|||||||
|
|
||||||
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
pyasic/data/error_codes/base.py
Normal file
18
pyasic/data/error_codes/base.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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()
|
||||||
@@ -14,11 +14,10 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from dataclasses import asdict, dataclass, fields
|
from pyasic.data.error_codes.base import BaseMinerError
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class BraiinsOSError(BaseMinerError):
|
||||||
class BraiinsOSError:
|
|
||||||
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -28,10 +27,3 @@ class BraiinsOSError:
|
|||||||
|
|
||||||
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,11 +14,13 @@
|
|||||||
# 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
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class InnosiliconError(BaseMinerError):
|
||||||
class InnosiliconError:
|
|
||||||
"""A Dataclass to handle error codes of Innosilicon miners.
|
"""A Dataclass to handle error codes of Innosilicon miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -27,25 +29,14 @@ class InnosiliconError:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
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): # noqa - Skip PyCharm inspection
|
def error_message(self) -> str: # 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 dataclasses import asdict, dataclass, field, fields
|
from pyasic.data.error_codes.base import BaseMinerError
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class WhatsminerError(BaseMinerError):
|
||||||
class WhatsminerError:
|
|
||||||
"""A Dataclass to handle error codes of Whatsminers.
|
"""A Dataclass to handle error codes of Whatsminers.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -27,14 +27,10 @@ class WhatsminerError:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
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): # noqa - Skip PyCharm inspection
|
def error_message(self) -> str: # 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])
|
||||||
@@ -74,13 +70,6 @@ class WhatsminerError:
|
|||||||
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:
|
class Fan(BaseModel):
|
||||||
"""A Dataclass to standardize fan data.
|
"""A Dataclass to standardize fan data.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
from enum import Enum
|
|
||||||
|
|
||||||
from pyasic.data.hashrate.sha256 import SHA256HashRate
|
|
||||||
from pyasic.device.algorithm.sha256 import SHA256Unit
|
|
||||||
|
|
||||||
|
|
||||||
class AlgoHashRate(Enum):
|
|
||||||
SHA256 = SHA256HashRate
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
return self.value(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class HashUnit:
|
|
||||||
SHA256 = SHA256Unit
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
from pyasic.device.algorithm import MinerAlgo
|
|
||||||
from pyasic.device.algorithm.sha256 import SHA256Unit
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class SHA256HashRate:
|
|
||||||
rate: float
|
|
||||||
unit: SHA256Unit = MinerAlgo.SHA256.unit.default
|
|
||||||
|
|
||||||
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: SHA256HashRate | int | float) -> SHA256HashRate:
|
|
||||||
if isinstance(other, SHA256HashRate):
|
|
||||||
return SHA256HashRate(self.rate + other.into(self.unit).rate, self.unit)
|
|
||||||
return SHA256HashRate(self.rate + other, self.unit)
|
|
||||||
|
|
||||||
def __sub__(self, other: SHA256HashRate | int | float) -> SHA256HashRate:
|
|
||||||
if isinstance(other, SHA256HashRate):
|
|
||||||
return SHA256HashRate(self.rate - other.into(self.unit).rate, self.unit)
|
|
||||||
return SHA256HashRate(self.rate - other, self.unit)
|
|
||||||
|
|
||||||
def __truediv__(self, other: SHA256HashRate | int | float):
|
|
||||||
if isinstance(other, SHA256HashRate):
|
|
||||||
return SHA256HashRate(self.rate / other.into(self.unit).rate, self.unit)
|
|
||||||
return SHA256HashRate(self.rate / other, self.unit)
|
|
||||||
|
|
||||||
def __floordiv__(self, other: SHA256HashRate | int | float):
|
|
||||||
if isinstance(other, SHA256HashRate):
|
|
||||||
return SHA256HashRate(self.rate // other.into(self.unit).rate, self.unit)
|
|
||||||
return SHA256HashRate(self.rate // other, self.unit)
|
|
||||||
|
|
||||||
def __mul__(self, other: SHA256HashRate | int | float):
|
|
||||||
if isinstance(other, SHA256HashRate):
|
|
||||||
return SHA256HashRate(self.rate * other.into(self.unit).rate, self.unit)
|
|
||||||
return SHA256HashRate(self.rate * other, self.unit)
|
|
||||||
|
|
||||||
def into(self, other: SHA256Unit) -> SHA256HashRate:
|
|
||||||
return SHA256HashRate(
|
|
||||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
|
||||||
)
|
|
||||||
@@ -1,21 +1,27 @@
|
|||||||
from dataclasses import dataclass, field
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from pydantic import BaseModel, computed_field, model_serializer
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
|
|
||||||
class Scheme(Enum):
|
class Scheme(Enum):
|
||||||
STRATUM_V1 = "stratum+tcp"
|
STRATUM_V1 = "stratum+tcp"
|
||||||
STRATUM_V2 = "stratum2+tcp"
|
STRATUM_V2 = "stratum2+tcp"
|
||||||
|
STRATUM_V1_SSL = "stratum+ssl"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class PoolUrl(BaseModel):
|
||||||
class PoolUrl:
|
|
||||||
scheme: Scheme
|
scheme: Scheme
|
||||||
host: str
|
host: str
|
||||||
port: int
|
port: int
|
||||||
pubkey: Optional[str] = None
|
pubkey: Optional[str] = None
|
||||||
|
|
||||||
|
@model_serializer
|
||||||
|
def serialize(self):
|
||||||
|
return str(self)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.scheme == Scheme.STRATUM_V2 and self.pubkey:
|
if self.scheme == Scheme.STRATUM_V2 and self.pubkey:
|
||||||
return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}"
|
return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}"
|
||||||
@@ -23,17 +29,21 @@ class PoolUrl:
|
|||||||
return f"{self.scheme.value}://{self.host}:{self.port}"
|
return f"{self.scheme.value}://{self.host}:{self.port}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_str(cls, url: str) -> "PoolUrl":
|
def from_str(cls, url: str) -> Self | None:
|
||||||
parsed_url = urlparse(url)
|
parsed_url = urlparse(url)
|
||||||
scheme = Scheme(parsed_url.scheme)
|
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
|
host = parsed_url.hostname
|
||||||
port = parsed_url.port
|
port = parsed_url.port
|
||||||
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
|
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
|
||||||
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
|
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class PoolMetrics(BaseModel):
|
||||||
class PoolMetrics:
|
|
||||||
"""A dataclass to standardize pool metrics returned from miners.
|
"""A dataclass to standardize pool metrics returned from miners.
|
||||||
Attributes:
|
Attributes:
|
||||||
|
|
||||||
@@ -50,27 +60,23 @@ class PoolMetrics:
|
|||||||
pool_stale_percent: Percentage of stale shares by the pool.
|
pool_stale_percent: Percentage of stale shares by the pool.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url: PoolUrl
|
url: PoolUrl | None
|
||||||
accepted: int = None
|
accepted: int | None = None
|
||||||
rejected: int = None
|
rejected: int | None = None
|
||||||
get_failures: int = None
|
get_failures: int | None = None
|
||||||
remote_failures: int = None
|
remote_failures: int | None = None
|
||||||
active: bool = None
|
active: bool | None = None
|
||||||
alive: bool = None
|
alive: bool | None = None
|
||||||
index: int = None
|
index: int | None = None
|
||||||
user: str = None
|
user: str | None = None
|
||||||
pool_rejected_percent: float = field(init=False)
|
|
||||||
pool_stale_percent: float = field(init=False)
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[misc]
|
||||||
@property
|
@property
|
||||||
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
|
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
|
||||||
"""Calculate and return the percentage of rejected shares"""
|
"""Calculate and return the percentage of rejected shares"""
|
||||||
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
|
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
|
||||||
|
|
||||||
@pool_rejected_percent.setter
|
@computed_field # type: ignore[misc]
|
||||||
def pool_rejected_percent(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
|
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
|
||||||
"""Calculate and return the percentage of stale shares."""
|
"""Calculate and return the percentage of stale shares."""
|
||||||
@@ -78,13 +84,11 @@ class PoolMetrics:
|
|||||||
self.get_failures, self.accepted + self.rejected
|
self.get_failures, self.accepted + self.rejected
|
||||||
)
|
)
|
||||||
|
|
||||||
@pool_stale_percent.setter
|
|
||||||
def pool_stale_percent(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _calculate_percentage(value: int, total: int) -> float:
|
def _calculate_percentage(value: int, total: int) -> float:
|
||||||
"""Calculate the percentage."""
|
"""Calculate the percentage."""
|
||||||
|
if value is None or total is None:
|
||||||
|
return 0
|
||||||
if total == 0:
|
if total == 0:
|
||||||
return 0
|
return 0
|
||||||
return (value / total) * 100
|
return (value / total) * 100
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
from pyasic.device.algorithm.sha256 import SHA256Algo
|
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:
|
class MinerAlgo:
|
||||||
SHA256 = SHA256Algo
|
SHA256 = SHA256Algo
|
||||||
|
SCRYPT = ScryptAlgo
|
||||||
|
KHEAVYHASH = KHeavyHashAlgo
|
||||||
|
KADENA = KadenaAlgo
|
||||||
|
HANDSHAKE = HandshakeAlgo
|
||||||
|
X11 = X11Algo
|
||||||
|
BLAKE256 = Blake256Algo
|
||||||
|
EAGLESONG = EaglesongAlgo
|
||||||
|
ETHASH = EtHashAlgo
|
||||||
|
EQUIHASH = EquihashAlgo
|
||||||
|
|||||||
16
pyasic/device/algorithm/base.py
Normal file
16
pyasic/device/algorithm/base.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .hashrate.base import AlgoHashRateType
|
||||||
|
from .hashrate.unit.base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class MinerAlgoMeta(type):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def __str__(cls):
|
||||||
|
return cls.name
|
||||||
|
|
||||||
|
|
||||||
|
class MinerAlgoType(metaclass=MinerAlgoMeta):
|
||||||
|
hashrate: type[AlgoHashRateType]
|
||||||
|
unit: type[AlgoHashRateUnitType]
|
||||||
13
pyasic/device/algorithm/blake256.py
Normal file
13
pyasic/device/algorithm/blake256.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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"
|
||||||
13
pyasic/device/algorithm/eaglesong.py
Normal file
13
pyasic/device/algorithm/eaglesong.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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"
|
||||||
13
pyasic/device/algorithm/equihash.py
Normal file
13
pyasic/device/algorithm/equihash.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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"
|
||||||
12
pyasic/device/algorithm/ethash.py
Normal file
12
pyasic/device/algorithm/ethash.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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"
|
||||||
13
pyasic/device/algorithm/handshake.py
Normal file
13
pyasic/device/algorithm/handshake.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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"
|
||||||
24
pyasic/device/algorithm/hashrate/__init__.py
Normal file
24
pyasic/device/algorithm/hashrate/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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
|
||||||
95
pyasic/device/algorithm/hashrate/base.py
Normal file
95
pyasic/device/algorithm/hashrate/base.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/blake256.py
Normal file
18
pyasic/device/algorithm/hashrate/blake256.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/eaglesong.py
Normal file
18
pyasic/device/algorithm/hashrate/eaglesong.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/equihash.py
Normal file
18
pyasic/device/algorithm/hashrate/equihash.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/ethash.py
Normal file
18
pyasic/device/algorithm/hashrate/ethash.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/handshake.py
Normal file
18
pyasic/device/algorithm/hashrate/handshake.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/kadena.py
Normal file
18
pyasic/device/algorithm/hashrate/kadena.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/kheavyhash.py
Normal file
18
pyasic/device/algorithm/hashrate/kheavyhash.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/scrypt.py
Normal file
18
pyasic/device/algorithm/hashrate/scrypt.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
18
pyasic/device/algorithm/hashrate/sha256.py
Normal file
18
pyasic/device/algorithm/hashrate/sha256.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
23
pyasic/device/algorithm/hashrate/unit/__init__.py
Normal file
23
pyasic/device/algorithm/hashrate/unit/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
71
pyasic/device/algorithm/hashrate/unit/base.py
Normal file
71
pyasic/device/algorithm/hashrate/unit/base.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/blake256.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/blake256.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/eaglesong.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/eaglesong.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
34
pyasic/device/algorithm/hashrate/unit/equihash.py
Normal file
34
pyasic/device/algorithm/hashrate/unit/equihash.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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"
|
||||||
16
pyasic/device/algorithm/hashrate/unit/ethash.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/ethash.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/handshake.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/handshake.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class HandshakeUnit(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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/kadena.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/kadena.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class KadenaUnit(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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/kheavyhash.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/kheavyhash.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class KHeavyHashUnit(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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/scrypt.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/scrypt.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class ScryptUnit(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 = GH
|
||||||
16
pyasic/device/algorithm/hashrate/unit/sha256.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/sha256.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class SHA256Unit(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
|
||||||
16
pyasic/device/algorithm/hashrate/unit/x11.py
Normal file
16
pyasic/device/algorithm/hashrate/unit/x11.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import AlgoHashRateUnitType
|
||||||
|
|
||||||
|
|
||||||
|
class X11Unit(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 = GH
|
||||||
18
pyasic/device/algorithm/hashrate/x11.py
Normal file
18
pyasic/device/algorithm/hashrate/x11.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
|
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
|
||||||
|
from pyasic.device.algorithm.hashrate.unit.x11 import X11Unit
|
||||||
|
|
||||||
|
from .unit import HashUnit
|
||||||
|
|
||||||
|
|
||||||
|
class X11HashRate(AlgoHashRateType):
|
||||||
|
rate: float
|
||||||
|
unit: X11Unit = HashUnit.X11.default
|
||||||
|
|
||||||
|
def into(self, other: X11Unit) -> Self:
|
||||||
|
return self.__class__(
|
||||||
|
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||||
|
)
|
||||||
13
pyasic/device/algorithm/kadena.py
Normal file
13
pyasic/device/algorithm/kadena.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import KadenaHashRate
|
||||||
|
from .hashrate.unit import KadenaUnit
|
||||||
|
|
||||||
|
|
||||||
|
# make this json serializable
|
||||||
|
class KadenaAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[KadenaHashRate] = KadenaHashRate
|
||||||
|
unit: type[KadenaUnit] = KadenaUnit
|
||||||
|
|
||||||
|
name = "Kadena"
|
||||||
13
pyasic/device/algorithm/kheavyhash.py
Normal file
13
pyasic/device/algorithm/kheavyhash.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import KHeavyHashHashRate
|
||||||
|
from .hashrate.unit import KHeavyHashUnit
|
||||||
|
|
||||||
|
|
||||||
|
# make this json serializable
|
||||||
|
class KHeavyHashAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[KHeavyHashHashRate] = KHeavyHashHashRate
|
||||||
|
unit: type[KHeavyHashUnit] = KHeavyHashUnit
|
||||||
|
|
||||||
|
name = "KHeavyHash"
|
||||||
12
pyasic/device/algorithm/scrypt.py
Normal file
12
pyasic/device/algorithm/scrypt.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import ScryptHashRate
|
||||||
|
from .hashrate.unit import ScryptUnit
|
||||||
|
|
||||||
|
|
||||||
|
class ScryptAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[ScryptHashRate] = ScryptHashRate
|
||||||
|
unit: type[ScryptUnit] = ScryptUnit
|
||||||
|
|
||||||
|
name = "Scrypt"
|
||||||
@@ -1,68 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import IntEnum
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import SHA256HashRate
|
||||||
|
from .hashrate.unit import SHA256Unit
|
||||||
class SHA256Unit(IntEnum):
|
|
||||||
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
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
# make this json serializable
|
# make this json serializable
|
||||||
class _SHA256Algo(str):
|
class SHA256Algo(MinerAlgoType):
|
||||||
unit = SHA256Unit
|
hashrate: type[SHA256HashRate] = SHA256HashRate
|
||||||
|
unit: type[SHA256Unit] = SHA256Unit
|
||||||
|
|
||||||
def __repr__(self):
|
name = "SHA256"
|
||||||
return "SHA256Algo"
|
|
||||||
|
|
||||||
|
|
||||||
SHA256Algo = _SHA256Algo("SHA256")
|
|
||||||
|
|||||||
13
pyasic/device/algorithm/x11.py
Normal file
13
pyasic/device/algorithm/x11.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import X11HashRate
|
||||||
|
from .hashrate.unit import X11Unit
|
||||||
|
|
||||||
|
|
||||||
|
# make this json serializable
|
||||||
|
class X11Algo(MinerAlgoType):
|
||||||
|
hashrate: type[X11HashRate] = X11HashRate
|
||||||
|
unit: type[X11Unit] = X11Unit
|
||||||
|
|
||||||
|
name = "X11"
|
||||||
@@ -26,6 +26,8 @@ class MinerMake(str, Enum):
|
|||||||
AURADINE = "Auradine"
|
AURADINE = "Auradine"
|
||||||
EPIC = "ePIC"
|
EPIC = "ePIC"
|
||||||
BITAXE = "BitAxe"
|
BITAXE = "BitAxe"
|
||||||
|
ICERIVER = "IceRiver"
|
||||||
|
HAMMER = "Hammer"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class AntminerModels(str, Enum):
|
class MinerModelType(str, Enum):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AntminerModels(MinerModelType):
|
||||||
D3 = "D3"
|
D3 = "D3"
|
||||||
HS3 = "HS3"
|
HS3 = "HS3"
|
||||||
L3Plus = "L3+"
|
L3Plus = "L3+"
|
||||||
|
KA3 = "KA3"
|
||||||
|
KS3 = "KS3"
|
||||||
DR5 = "DR5"
|
DR5 = "DR5"
|
||||||
|
KS5 = "KS5"
|
||||||
L7 = "L7"
|
L7 = "L7"
|
||||||
|
K7 = "K7"
|
||||||
|
D7 = "D7"
|
||||||
E9Pro = "E9Pro"
|
E9Pro = "E9Pro"
|
||||||
S9 = "S9"
|
S9 = "S9"
|
||||||
S9i = "S9i"
|
S9i = "S9i"
|
||||||
S9j = "S9j"
|
S9j = "S9j"
|
||||||
T9 = "T9"
|
T9 = "T9"
|
||||||
|
D9 = "D9"
|
||||||
|
L9 = "L9"
|
||||||
Z15 = "Z15"
|
Z15 = "Z15"
|
||||||
|
Z15Pro = "Z15 Pro"
|
||||||
S17 = "S17"
|
S17 = "S17"
|
||||||
S17Plus = "S17+"
|
S17Plus = "S17+"
|
||||||
S17Pro = "S17 Pro"
|
S17Pro = "S17 Pro"
|
||||||
@@ -51,7 +63,7 @@ class AntminerModels(str, Enum):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class WhatsminerModels(str, Enum):
|
class WhatsminerModels(MinerModelType):
|
||||||
M20V10 = "M20 V10"
|
M20V10 = "M20 V10"
|
||||||
M20SV10 = "M20S V10"
|
M20SV10 = "M20S V10"
|
||||||
M20SV20 = "M20S V20"
|
M20SV20 = "M20S V20"
|
||||||
@@ -220,6 +232,7 @@ class WhatsminerModels(str, Enum):
|
|||||||
M50VH60 = "M50 VH60"
|
M50VH60 = "M50 VH60"
|
||||||
M50VH70 = "M50 VH70"
|
M50VH70 = "M50 VH70"
|
||||||
M50VH80 = "M50 VH80"
|
M50VH80 = "M50 VH80"
|
||||||
|
M50VH90 = "M50 VH90"
|
||||||
M50VJ10 = "M50 VJ10"
|
M50VJ10 = "M50 VJ10"
|
||||||
M50VJ20 = "M50 VJ20"
|
M50VJ20 = "M50 VJ20"
|
||||||
M50VJ30 = "M50 VJ30"
|
M50VJ30 = "M50 VJ30"
|
||||||
@@ -271,7 +284,7 @@ class WhatsminerModels(str, Enum):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class AvalonminerModels(str, Enum):
|
class AvalonminerModels(MinerModelType):
|
||||||
Avalon721 = "Avalon 721"
|
Avalon721 = "Avalon 721"
|
||||||
Avalon741 = "Avalon 741"
|
Avalon741 = "Avalon 741"
|
||||||
Avalon761 = "Avalon 761"
|
Avalon761 = "Avalon 761"
|
||||||
@@ -283,6 +296,7 @@ class AvalonminerModels(str, Enum):
|
|||||||
Avalon1047 = "Avalon 1047"
|
Avalon1047 = "Avalon 1047"
|
||||||
Avalon1066 = "Avalon 1066"
|
Avalon1066 = "Avalon 1066"
|
||||||
Avalon1166Pro = "Avalon 1166 Pro"
|
Avalon1166Pro = "Avalon 1166 Pro"
|
||||||
|
Avalon1126Pro = "Avalon 1126 Pro"
|
||||||
Avalon1246 = "Avalon 1246"
|
Avalon1246 = "Avalon 1246"
|
||||||
AvalonNano3 = "Avalon Nano 3"
|
AvalonNano3 = "Avalon Nano 3"
|
||||||
|
|
||||||
@@ -290,15 +304,17 @@ class AvalonminerModels(str, Enum):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class InnosiliconModels(str, Enum):
|
class InnosiliconModels(MinerModelType):
|
||||||
T3HPlus = "T3H+"
|
T3HPlus = "T3H+"
|
||||||
A10X = "A10X"
|
A10X = "A10X"
|
||||||
|
A11 = "A11"
|
||||||
|
A11MX = "A11MX"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class GoldshellModels(str, Enum):
|
class GoldshellModels(MinerModelType):
|
||||||
CK5 = "CK5"
|
CK5 = "CK5"
|
||||||
HS5 = "HS5"
|
HS5 = "HS5"
|
||||||
KD5 = "KD5"
|
KD5 = "KD5"
|
||||||
@@ -310,7 +326,7 @@ class GoldshellModels(str, Enum):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class ePICModels(str, Enum):
|
class ePICModels(MinerModelType):
|
||||||
BM520i = "BlockMiner 520i"
|
BM520i = "BlockMiner 520i"
|
||||||
BM720i = "BlockMiner 720i"
|
BM720i = "BlockMiner 720i"
|
||||||
|
|
||||||
@@ -318,7 +334,7 @@ class ePICModels(str, Enum):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class AuradineModels(str, Enum):
|
class AuradineModels(MinerModelType):
|
||||||
AT1500 = "AT1500"
|
AT1500 = "AT1500"
|
||||||
AT2860 = "AT2860"
|
AT2860 = "AT2860"
|
||||||
AT2880 = "AT2880"
|
AT2880 = "AT2880"
|
||||||
@@ -331,17 +347,33 @@ class AuradineModels(str, Enum):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class BitAxeModels(str, Enum):
|
class BitAxeModels(MinerModelType):
|
||||||
BM1366 = "Ultra"
|
BM1366 = "Ultra"
|
||||||
BM1368 = "Supra"
|
BM1368 = "Supra"
|
||||||
BM1397 = "Max"
|
BM1397 = "Max"
|
||||||
|
BM1370 = "Gamma"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class IceRiverModels(str, Enum):
|
class IceRiverModels(MinerModelType):
|
||||||
|
KS0 = "KS0"
|
||||||
|
KS1 = "KS1"
|
||||||
KS2 = "KS2"
|
KS2 = "KS2"
|
||||||
|
KS3 = "KS3"
|
||||||
|
KS3L = "KS3L"
|
||||||
|
KS3M = "KS3M"
|
||||||
|
KS5 = "KS5"
|
||||||
|
KS5L = "KS5L"
|
||||||
|
KS5M = "KS5M"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
class HammerModels(MinerModelType):
|
||||||
|
D10 = "D10"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
@@ -357,3 +389,4 @@ class MinerModel:
|
|||||||
EPIC = ePICModels
|
EPIC = ePICModels
|
||||||
BITAXE = BitAxeModels
|
BITAXE = BitAxeModels
|
||||||
ICERIVER = IceRiverModels
|
ICERIVER = IceRiverModels
|
||||||
|
HAMMER = HammerModels
|
||||||
|
|||||||
22
pyasic/miners/antminer/bmminer/X15/Z15.py
Normal file
22
pyasic/miners/antminer/bmminer/X15/Z15.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import Z15Pro
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerZ15Pro(AntminerModern, Z15Pro):
|
||||||
|
pass
|
||||||
16
pyasic/miners/antminer/bmminer/X15/__init__.py
Normal file
16
pyasic/miners/antminer/bmminer/X15/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 .Z15 import BMMinerZ15Pro
|
||||||
@@ -23,6 +23,7 @@ from .S19 import (
|
|||||||
BMMinerS19j,
|
BMMinerS19j,
|
||||||
BMMinerS19jNoPIC,
|
BMMinerS19jNoPIC,
|
||||||
BMMinerS19jPro,
|
BMMinerS19jPro,
|
||||||
|
BMMinerS19KPro,
|
||||||
BMMinerS19L,
|
BMMinerS19L,
|
||||||
BMMinerS19Plus,
|
BMMinerS19Plus,
|
||||||
BMMinerS19Pro,
|
BMMinerS19Pro,
|
||||||
@@ -30,6 +31,5 @@ from .S19 import (
|
|||||||
BMMinerS19ProPlus,
|
BMMinerS19ProPlus,
|
||||||
BMMinerS19ProPlusHydro,
|
BMMinerS19ProPlusHydro,
|
||||||
BMMinerS19XP,
|
BMMinerS19XP,
|
||||||
BMMinerS19KPro,
|
|
||||||
)
|
)
|
||||||
from .T19 import BMMinerT19
|
from .T19 import BMMinerT19
|
||||||
|
|||||||
22
pyasic/miners/antminer/bmminer/X3/KA3.py
Normal file
22
pyasic/miners/antminer/bmminer/X3/KA3.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import KA3
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerKA3(AntminerModern, KA3):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/bmminer/X3/KS3.py
Normal file
22
pyasic/miners/antminer/bmminer/X3/KS3.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models.antminer import KS3
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerKS3(AntminerModern, KS3):
|
||||||
|
pass
|
||||||
@@ -14,4 +14,6 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from .HS3 import BMMinerHS3
|
from .HS3 import BMMinerHS3
|
||||||
|
from .KA3 import BMMinerKA3
|
||||||
|
from .KS3 import BMMinerKS3
|
||||||
from .L3 import BMMinerL3Plus
|
from .L3 import BMMinerL3Plus
|
||||||
|
|||||||
21
pyasic/miners/antminer/bmminer/X5/KS5.py
Normal file
21
pyasic/miners/antminer/bmminer/X5/KS5.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import KS5
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerKS5(AntminerModern, KS5):
|
||||||
|
supports_shutdown = False
|
||||||
16
pyasic/miners/antminer/bmminer/X5/__init__.py
Normal file
16
pyasic/miners/antminer/bmminer/X5/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 .KS5 import BMMinerKS5
|
||||||
21
pyasic/miners/antminer/bmminer/X7/D7.py
Normal file
21
pyasic/miners/antminer/bmminer/X7/D7.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import D7
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerD7(AntminerModern, D7):
|
||||||
|
pass
|
||||||
21
pyasic/miners/antminer/bmminer/X7/K7.py
Normal file
21
pyasic/miners/antminer/bmminer/X7/K7.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import K7
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerK7(AntminerModern, K7):
|
||||||
|
pass
|
||||||
@@ -13,4 +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. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
from .D7 import BMMinerD7
|
||||||
|
from .K7 import BMMinerK7
|
||||||
from .L7 import BMMinerL7
|
from .L7 import BMMinerL7
|
||||||
|
|||||||
22
pyasic/miners/antminer/bmminer/X9/D9.py
Normal file
22
pyasic/miners/antminer/bmminer/X9/D9.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import D9
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerD9(AntminerModern, D9):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/bmminer/X9/L9.py
Normal file
22
pyasic/miners/antminer/bmminer/X9/L9.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 pyasic.miners.backends import AntminerModern
|
||||||
|
from pyasic.miners.device.models import L9
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerL9(AntminerModern, L9):
|
||||||
|
pass
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .D9 import BMMinerD9
|
||||||
from .E9 import BMMinerE9Pro
|
from .E9 import BMMinerE9Pro
|
||||||
|
from .L9 import BMMinerL9
|
||||||
from .S9 import BMMinerS9, BMMinerS9i, BMMinerS9j
|
from .S9 import BMMinerS9, BMMinerS9i, BMMinerS9j
|
||||||
from .T9 import BMMinerT9
|
from .T9 import BMMinerT9
|
||||||
|
|||||||
@@ -14,8 +14,10 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from .X3 import *
|
from .X3 import *
|
||||||
|
from .X5 import *
|
||||||
from .X7 import *
|
from .X7 import *
|
||||||
from .X9 import *
|
from .X9 import *
|
||||||
|
from .X15 import *
|
||||||
from .X17 import *
|
from .X17 import *
|
||||||
from .X19 import *
|
from .X19 import *
|
||||||
from .X21 import *
|
from .X21 import *
|
||||||
|
|||||||
@@ -15,7 +15,4 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from .S21 import ePICS21, ePICS21Pro
|
from .S21 import ePICS21, ePICS21Pro
|
||||||
|
from .T21 import ePICT21
|
||||||
from .T21 import (
|
|
||||||
ePICT21,
|
|
||||||
)
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user