Compare commits

..

3 Commits

Author SHA1 Message Date
Upstream Data
54f0292712 bug: try to fix scan timeout with asyncio.wait_for. 2024-05-13 08:28:12 -06:00
Upstream Data
46c56134f7 docs: update docs generation to fix marathon miners. 2024-04-29 09:14:08 -06:00
Upstream Data
1c1f7f1098 bug: move client into web ping to try to fix scanning failing. 2024-04-26 13:07:08 -06:00
485 changed files with 2933 additions and 12497 deletions

View File

@@ -1,15 +1,12 @@
ci:
skip:
- unittest
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v4.5.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: check-yaml - id: check-yaml
- id: check-added-large-files - id: check-added-large-files
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 24.10.0 rev: 24.3.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
@@ -27,3 +24,4 @@ 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]

View File

@@ -42,7 +42,7 @@ It is recommended to install `pyasic` in a [virtual environment](https://realpyt
##### Installing `pyasic` ##### Installing `pyasic`
`python -m pip install pyasic` or `poetry install` `python -m pip install .` or `poetry install`
##### Additional Developer Setup ##### Additional Developer Setup
``` ```
@@ -255,30 +255,24 @@ if __name__ == "__main__":
```python ```python
from pyasic import settings from pyasic import settings
settings.update("default_antminer_web_password", "my_pwd") settings.update("default_antminer_password", "my_pwd")
``` ```
##### Default values: ##### Default values:
``` ```
"network_ping_retries": 1, "network_ping_retries": 1,
"network_ping_timeout": 3, "network_ping_timeout": 3,
"network_scan_semaphore": None, "network_scan_threads": 300,
"factory_get_retries": 1, "factory_get_retries": 1,
"factory_get_timeout": 3, "factory_get_timeout": 3,
"get_data_retries": 1, "get_data_retries": 1,
"api_function_timeout": 5, "api_function_timeout": 5,
"antminer_mining_mode_as_str": False, "default_whatsminer_password": "admin",
"default_whatsminer_rpc_password": "admin", "default_innosilicon_password": "admin",
"default_innosilicon_web_password": "admin", "default_antminer_password": "root",
"default_antminer_web_password": "root", "default_bosminer_password": "root",
"default_bosminer_web_password": "root", "default_vnish_password": "admin",
"default_vnish_web_password": "admin", "default_goldshell_password": "123456789",
"default_goldshell_web_password": "123456789",
"default_auradine_web_password": "admin",
"default_epic_web_password": "letmein",
"default_hive_web_password": "admin",
"default_antminer_ssh_password": "miner",
"default_bosminer_ssh_password": "root",
# ADVANCED # ADVANCED
# Only use this if you know what you are doing # Only use this if you know what you are doing

View File

@@ -49,12 +49,6 @@ def backend_str(backend: MinerTypes) -> str:
return "LuxOS Firmware Miners" return "LuxOS Firmware Miners"
case MinerTypes.MARATHON: case MinerTypes.MARATHON:
return "Mara Firmware Miners" return "Mara Firmware Miners"
case MinerTypes.BITAXE:
return "Stock Firmware BitAxe Miners"
case MinerTypes.ICERIVER:
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):

View File

@@ -41,7 +41,7 @@ It is recommended to install `pyasic` in a [virtual environment](https://realpyt
##### Installing `pyasic` ##### Installing `pyasic`
`python -m pip install pyasic` or `poetry install` `python -m pip install .` or `poetry install`
--- ---
## Getting started ## Getting started
@@ -249,30 +249,25 @@ if __name__ == "__main__":
```python ```python
from pyasic import settings from pyasic import settings
settings.update("default_antminer_web_password", "my_pwd") settings.update("default_antminer_password", "my_pwd")
``` ```
##### Default values: ##### Default values:
``` ```
"network_ping_retries": 1, "network_ping_retries": 1,
"network_ping_timeout": 3, "network_ping_timeout": 3,
"network_scan_semaphore": None, "network_scan_threads": 300,
"factory_get_retries": 1, "factory_get_retries": 1,
"factory_get_timeout": 3, "factory_get_timeout": 3,
"get_data_retries": 1, "get_data_retries": 1,
"api_function_timeout": 5, "api_function_timeout": 5,
"antminer_mining_mode_as_str": False, "antminer_mining_mode_as_str": False,
"default_whatsminer_rpc_password": "admin", "default_whatsminer_password": "admin",
"default_innosilicon_web_password": "admin", "default_innosilicon_password": "admin",
"default_antminer_web_password": "root", "default_antminer_password": "root",
"default_bosminer_web_password": "root", "default_bosminer_password": "root",
"default_vnish_web_password": "admin", "default_vnish_password": "admin",
"default_goldshell_web_password": "123456789", "default_goldshell_password": "123456789",
"default_auradine_web_password": "admin",
"default_epic_web_password": "letmein",
"default_hive_web_password": "admin",
"default_antminer_ssh_password": "miner",
"default_bosminer_ssh_password": "root",
# ADVANCED # ADVANCED
# Only use this if you know what you are doing # Only use this if you know what you are doing

View File

@@ -1,17 +1,10 @@
# pyasic # pyasic
## X15 Models ## X15 Models
## Z15 (Stock) ## Z15
::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15 ::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Z15 Pro (Stock)
::: pyasic.miners.antminer.bmminer.X15.Z15.BMMinerZ15Pro
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,49 +1,49 @@
# pyasic # pyasic
## X17 Models ## X17 Models
## S17 (Stock) ## S17
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17 ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17+ (Stock) ## S17+
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17 Pro (Stock) ## S17 Pro
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17e (Stock) ## S17e
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T17 (Stock) ## T17
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17 ::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T17+ (Stock) ## T17+
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus ::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T17e (Stock) ## T17e
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e ::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e
handler: python handler: python
options: options:

View File

@@ -1,238 +1,224 @@
# pyasic # pyasic
## X19 Models ## X19 Models
## S19 (Stock) ## S19
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19 ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19L (Stock) ## S19L
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro (Stock) ## S19 Pro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Pro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Pro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j (Stock) ## S19j
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19i (Stock) ## S19i
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19+ (Stock) ## S19+
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j No PIC (Stock) ## S19j No PIC
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro+ (Stock) ## S19 Pro+
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro (Stock) ## S19j Pro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 XP (Stock) ## S19 XP
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19a (Stock) ## S19a
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19a Pro (Stock) ## S19a Pro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Hydro (Stock) ## S19 Hydro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro Hydro (Stock) ## S19 Pro Hydro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProHydro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProHydro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro+ Hydro (Stock) ## S19 Pro+ Hydro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlusHydro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlusHydro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19K Pro (Stock) ## S19K Pro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T19 (Stock) ## T19
::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19 ::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 (BOS+) ## S19
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19 ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19+ (BOS+) ## S19+
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro (BOS+) ## S19 Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Pro ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Pro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19a (BOS+) ## S19a
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19a ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19a
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19a Pro (BOS+) ## S19a Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19aPro ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19aPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j (BOS+) ## S19j
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19j ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19j
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j No PIC (BOS+) ## S19j No PIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro (BOS+) ## S19j Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro No PIC (BOS+) ## S19j Pro No PIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProNoPIC ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro+ (BOS+) ## S19j Pro+
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro+ (BOS+) ## S19j Pro+
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro+ No PIC (BOS+) ## S19j Pro+ No PIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlusNoPIC ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlusNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19k Pro No PIC (BOS+) ## S19k Pro No PIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19k Pro No PIC (BOS+) ## S19 XP
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 XP (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro+ Hydro (BOS+) ## T19
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19ProPlusHydro
handler: python
options:
show_root_heading: false
heading_level: 4
## T19 (BOS+)
::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19 ::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19
handler: python handler: python
options: options:
@@ -274,13 +260,6 @@
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,13 +274,6 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro Hydro (VNish)
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProHydro
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
@@ -358,13 +330,6 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro (Stock)
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19jPro
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

View File

@@ -1,48 +1,27 @@
# pyasic # pyasic
## X21 Models ## X21 Models
## S21 (Stock) ## S21
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21 ::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S21 Pro (Stock) ## T21
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Pro
handler: python
options:
show_root_heading: false
heading_level: 4
## T21 (Stock)
::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21 ::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S21 (BOS+) ## S21
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21 ::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 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)
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21 (ePIC) ## S21 (ePIC)
::: pyasic.miners.antminer.epic.X21.S21.ePICS21 ::: pyasic.miners.antminer.epic.X21.S21.ePICS21
handler: python handler: python
@@ -50,13 +29,6 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S21 Pro (ePIC)
::: pyasic.miners.antminer.epic.X21.S21.ePICS21Pro
handler: python
options:
show_root_heading: false
heading_level: 4
## T21 (ePIC) ## T21 (ePIC)
::: pyasic.miners.antminer.epic.X21.T21.ePICT21 ::: pyasic.miners.antminer.epic.X21.T21.ePICT21
handler: python handler: python

View File

@@ -1,41 +1,27 @@
# pyasic # pyasic
## X3 Models ## X3 Models
## D3 (Stock) ## D3
::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3 ::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## HS3 (Stock) ## HS3
::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3 ::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## L3+ (Stock) ## L3+
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus ::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 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

View File

@@ -1,17 +1,10 @@
# pyasic # pyasic
## X5 Models ## X5 Models
## DR5 (Stock) ## DR5
::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5 ::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## KS5 (Stock)
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,27 +1,13 @@
# pyasic # pyasic
## X7 Models ## X7 Models
## L7 (Stock) ## L7
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7 ::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 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

View File

@@ -1,42 +1,35 @@
# pyasic # pyasic
## X9 Models ## X9 Models
## E9Pro (Stock) ## E9Pro
::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro ::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## D9 (Stock) ## S9
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
handler: python
options:
show_root_heading: false
heading_level: 4
## S9 (Stock)
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9 ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S9i (Stock) ## S9i
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S9j (Stock) ## S9j
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T9 (Stock) ## T9
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9 ::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
handler: python handler: python
options: options:
@@ -50,7 +43,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:

View File

@@ -1,21 +1,21 @@
# pyasic # pyasic
## AD Models ## AD Models
## AT1500 (Stock) ## AT1500
::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500 ::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## AT2860 (Stock) ## AT2860
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860 ::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## AT2880 (Stock) ## AT2880
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880 ::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880
handler: python handler: python
options: options:

View File

@@ -1,14 +1,14 @@
# pyasic # pyasic
## AI Models ## AI Models
## AI2500 (Stock) ## AI2500
::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500 ::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## AI3680 (Stock) ## AI3680
::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680 ::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680
handler: python handler: python
options: options:

View File

@@ -1,14 +1,14 @@
# pyasic # pyasic
## AT Models ## AT Models
## AD2500 (Stock) ## AD2500
::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500 ::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## AD3500 (Stock) ## AD3500
::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500 ::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500
handler: python handler: python
options: options:

View File

@@ -1,21 +1,21 @@
# pyasic # pyasic
## A10X Models ## A10X Models
## Avalon 1026 (Stock) ## Avalon 1026
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026 ::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Avalon 1047 (Stock) ## Avalon 1047
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047 ::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Avalon 1066 (Stock) ## Avalon 1066
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066 ::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
handler: python handler: python
options: options:

View File

@@ -1,7 +1,7 @@
# pyasic # pyasic
## A11X Models ## A11X Models
## Avalon 1166 Pro (Stock) ## Avalon 1166 Pro
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro ::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
handler: python handler: python
options: options:

View File

@@ -1,7 +1,7 @@
# pyasic # pyasic
## A12X Models ## A12X Models
## Avalon 1246 (Stock) ## Avalon 1246
::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246 ::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246
handler: python handler: python
options: options:

View File

@@ -1,21 +1,21 @@
# pyasic # pyasic
## A7X Models ## A7X Models
## Avalon 721 (Stock) ## Avalon 721
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721 ::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Avalon 741 (Stock) ## Avalon 741
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741 ::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Avalon 761 (Stock) ## Avalon 761
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761 ::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
handler: python handler: python
options: options:

View File

@@ -1,21 +1,21 @@
# pyasic # pyasic
## A8X Models ## A8X Models
## Avalon 821 (Stock) ## Avalon 821
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821 ::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Avalon 841 (Stock) ## Avalon 841
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841 ::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Avalon 851 (Stock) ## Avalon 851
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851 ::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
handler: python handler: python
options: options:

View File

@@ -1,7 +1,7 @@
# pyasic # pyasic
## A9X Models ## A9X Models
## Avalon 921 (Stock) ## Avalon 921
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921 ::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
handler: python handler: python
options: options:

View File

@@ -1,10 +0,0 @@
# pyasic
## nano Models
## Avalon Nano 3 (Stock)
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,31 +0,0 @@
# pyasic
## BM Models
## Supra (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
handler: python
options:
show_root_heading: false
heading_level: 4
## Ultra (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1366.BitAxeUltra
handler: python
options:
show_root_heading: false
heading_level: 4
## Max (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
handler: python
options:
show_root_heading: false
heading_level: 4
## Gamma (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1370.BitAxeGamma
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,17 +0,0 @@
# pyasic
## blockminer Models
## BlockMiner 520i (ePIC)
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner520i
handler: python
options:
show_root_heading: false
heading_level: 4
## BlockMiner 720i (ePIC)
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner720i
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,21 +1,21 @@
# pyasic # pyasic
## X5 Models ## X5 Models
## CK5 (Stock) ## CK5
::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5 ::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## HS5 (Stock) ## HS5
::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5 ::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## KD5 (Stock) ## KD5
::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5 ::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5
handler: python handler: python
options: options:

View File

@@ -1,14 +1,14 @@
# pyasic # pyasic
## XBox Models ## XBox Models
## KD Box II (Stock) ## KD Box II
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII ::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## KD Box Pro (Stock) ## KD Box Pro
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro ::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro
handler: python handler: python
options: options:

View File

@@ -1,7 +1,7 @@
# pyasic # pyasic
## XMax Models ## XMax Models
## KD Max (Stock) ## KD Max
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax ::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax
handler: python handler: python
options: options:

View File

@@ -1,10 +0,0 @@
# pyasic
## DX Models
## D10 (Stock)
::: pyasic.miners.hammer.blackminer.DX.D10.HammerD10
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,66 +0,0 @@
# pyasic
## 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)
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
handler: python
options:
show_root_heading: false
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

View File

@@ -1,7 +1,7 @@
# pyasic # pyasic
## A10X Models ## A10X Models
## A10X (Stock) ## A10X
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X ::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
handler: python handler: python
options: options:

View File

@@ -1,17 +0,0 @@
# 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

View File

@@ -1,7 +1,7 @@
# pyasic # pyasic
## T3X Models ## T3X Models
## T3H+ (Stock) ## T3H+
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus ::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
handler: python handler: python
options: options:

View File

@@ -18,86 +18,78 @@ details {
<details> <details>
<summary>X3 Series:</summary> <summary>X3 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X3#d3-stock">D3 (Stock)</a></li> <li><a href="../antminer/X3#d3">D3</a></li>
<li><a href="../antminer/X3#hs3-stock">HS3 (Stock)</a></li> <li><a href="../antminer/X3#hs3">HS3</a></li>
<li><a href="../antminer/X3#l3_1-stock">L3+ (Stock)</a></li> <li><a href="../antminer/X3#l3_1">L3+</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">DR5</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">L7</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">E9Pro</a></li>
<li><a href="../antminer/X9#d9-stock">D9 (Stock)</a></li> <li><a href="../antminer/X9#s9">S9</a></li>
<li><a href="../antminer/X9#s9-stock">S9 (Stock)</a></li> <li><a href="../antminer/X9#s9i">S9i</a></li>
<li><a href="../antminer/X9#s9i-stock">S9i (Stock)</a></li> <li><a href="../antminer/X9#s9j">S9j</a></li>
<li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li> <li><a href="../antminer/X9#t9">T9</a></li>
<li><a href="../antminer/X9#t9-stock">T9 (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">Z15</a></li>
<li><a href="../antminer/X15#z15-pro-stock">Z15 Pro (Stock)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>X17 Series:</summary> <summary>X17 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X17#s17-stock">S17 (Stock)</a></li> <li><a href="../antminer/X17#s17">S17</a></li>
<li><a href="../antminer/X17#s17_1-stock">S17+ (Stock)</a></li> <li><a href="../antminer/X17#s17_1">S17+</a></li>
<li><a href="../antminer/X17#s17-pro-stock">S17 Pro (Stock)</a></li> <li><a href="../antminer/X17#s17-pro">S17 Pro</a></li>
<li><a href="../antminer/X17#s17e-stock">S17e (Stock)</a></li> <li><a href="../antminer/X17#s17e">S17e</a></li>
<li><a href="../antminer/X17#t17-stock">T17 (Stock)</a></li> <li><a href="../antminer/X17#t17">T17</a></li>
<li><a href="../antminer/X17#t17_1-stock">T17+ (Stock)</a></li> <li><a href="../antminer/X17#t17_1">T17+</a></li>
<li><a href="../antminer/X17#t17e-stock">T17e (Stock)</a></li> <li><a href="../antminer/X17#t17e">T17e</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>X19 Series:</summary> <summary>X19 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X19#s19-stock">S19 (Stock)</a></li> <li><a href="../antminer/X19#s19">S19</a></li>
<li><a href="../antminer/X19#s19l-stock">S19L (Stock)</a></li> <li><a href="../antminer/X19#s19l">S19L</a></li>
<li><a href="../antminer/X19#s19-pro-stock">S19 Pro (Stock)</a></li> <li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
<li><a href="../antminer/X19#s19j-stock">S19j (Stock)</a></li> <li><a href="../antminer/X19#s19j">S19j</a></li>
<li><a href="../antminer/X19#s19i-stock">S19i (Stock)</a></li> <li><a href="../antminer/X19#s19i">S19i</a></li>
<li><a href="../antminer/X19#s19_1-stock">S19+ (Stock)</a></li> <li><a href="../antminer/X19#s19_1">S19+</a></li>
<li><a href="../antminer/X19#s19j-no-pic-stock">S19j No PIC (Stock)</a></li> <li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
<li><a href="../antminer/X19#s19-pro_1-stock">S19 Pro+ (Stock)</a></li> <li><a href="../antminer/X19#s19-pro_1">S19 Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro-stock">S19j Pro (Stock)</a></li> <li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
<li><a href="../antminer/X19#s19-xp-stock">S19 XP (Stock)</a></li> <li><a href="../antminer/X19#s19-xp">S19 XP</a></li>
<li><a href="../antminer/X19#s19a-stock">S19a (Stock)</a></li> <li><a href="../antminer/X19#s19a">S19a</a></li>
<li><a href="../antminer/X19#s19a-pro-stock">S19a Pro (Stock)</a></li> <li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li>
<li><a href="../antminer/X19#s19-hydro-stock">S19 Hydro (Stock)</a></li> <li><a href="../antminer/X19#s19-hydro">S19 Hydro</a></li>
<li><a href="../antminer/X19#s19-pro-hydro-stock">S19 Pro Hydro (Stock)</a></li> <li><a href="../antminer/X19#s19-pro-hydro">S19 Pro Hydro</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro-stock">S19 Pro+ Hydro (Stock)</a></li> <li><a href="../antminer/X19#s19-pro_1-hydro">S19 Pro+ Hydro</a></li>
<li><a href="../antminer/X19#s19k-pro-stock">S19K Pro (Stock)</a></li> <li><a href="../antminer/X19#s19k-pro">S19K Pro</a></li>
<li><a href="../antminer/X19#t19-stock">T19 (Stock)</a></li> <li><a href="../antminer/X19#t19">T19</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>X21 Series:</summary> <summary>X21 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li> <li><a href="../antminer/X21#s21">S21</a></li>
<li><a href="../antminer/X21#s21-pro-stock">S21 Pro (Stock)</a></li> <li><a href="../antminer/X21#t21">T21</a></li>
<li><a href="../antminer/X21#t21-stock">T21 (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -108,235 +100,234 @@ details {
<details> <details>
<summary>M2X Series:</summary> <summary>M2X Series:</summary>
<ul> <ul>
<li><a href="../whatsminer/M2X#m20-v10-stock">M20 V10 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20-v10">M20 V10</a></li>
<li><a href="../whatsminer/M2X#m20s-v10-stock">M20S V10 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20s-v10">M20S V10</a></li>
<li><a href="../whatsminer/M2X#m20s-v20-stock">M20S V20 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20s-v20">M20S V20</a></li>
<li><a href="../whatsminer/M2X#m20s-v30-stock">M20S V30 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20s-v30">M20S V30</a></li>
<li><a href="../whatsminer/M2X#m20p-v10-stock">M20P V10 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20p-v10">M20P V10</a></li>
<li><a href="../whatsminer/M2X#m20p-v30-stock">M20P V30 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20p-v30">M20P V30</a></li>
<li><a href="../whatsminer/M2X#m20s_1-v30-stock">M20S+ V30 (Stock)</a></li> <li><a href="../whatsminer/M2X#m20s_1-v30">M20S+ V30</a></li>
<li><a href="../whatsminer/M2X#m21-v10-stock">M21 V10 (Stock)</a></li> <li><a href="../whatsminer/M2X#m21-v10">M21 V10</a></li>
<li><a href="../whatsminer/M2X#m21s-v20-stock">M21S V20 (Stock)</a></li> <li><a href="../whatsminer/M2X#m21s-v20">M21S V20</a></li>
<li><a href="../whatsminer/M2X#m21s-v60-stock">M21S V60 (Stock)</a></li> <li><a href="../whatsminer/M2X#m21s-v60">M21S V60</a></li>
<li><a href="../whatsminer/M2X#m21s-v70-stock">M21S V70 (Stock)</a></li> <li><a href="../whatsminer/M2X#m21s-v70">M21S V70</a></li>
<li><a href="../whatsminer/M2X#m21s_1-v20-stock">M21S+ V20 (Stock)</a></li> <li><a href="../whatsminer/M2X#m21s_1-v20">M21S+ V20</a></li>
<li><a href="../whatsminer/M2X#m29-v10-stock">M29 V10 (Stock)</a></li> <li><a href="../whatsminer/M2X#m29-v10">M29 V10</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>M3X Series:</summary> <summary>M3X Series:</summary>
<ul> <ul>
<li><a href="../whatsminer/M3X#m30-v10-stock">M30 V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30-v10">M30 V10</a></li>
<li><a href="../whatsminer/M3X#m30-v20-stock">M30 V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30-v20">M30 V20</a></li>
<li><a href="../whatsminer/M3X#m30k-v10-stock">M30K V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30k-v10">M30K V10</a></li>
<li><a href="../whatsminer/M3X#m30l-v10-stock">M30L V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
<li><a href="../whatsminer/M3X#m30s-v10-stock">M30S V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v10">M30S V10</a></li>
<li><a href="../whatsminer/M3X#m30s-v20-stock">M30S V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v20">M30S V20</a></li>
<li><a href="../whatsminer/M3X#m30s-v30-stock">M30S V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v30">M30S V30</a></li>
<li><a href="../whatsminer/M3X#m30s-v40-stock">M30S V40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v40">M30S V40</a></li>
<li><a href="../whatsminer/M3X#m30s-v50-stock">M30S V50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v50">M30S V50</a></li>
<li><a href="../whatsminer/M3X#m30s-v60-stock">M30S V60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v60">M30S V60</a></li>
<li><a href="../whatsminer/M3X#m30s-v70-stock">M30S V70 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v70">M30S V70</a></li>
<li><a href="../whatsminer/M3X#m30s-v80-stock">M30S V80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-v80">M30S V80</a></li>
<li><a href="../whatsminer/M3X#m30s-ve10-stock">M30S VE10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve10">M30S VE10</a></li>
<li><a href="../whatsminer/M3X#m30s-ve20-stock">M30S VE20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve20">M30S VE20</a></li>
<li><a href="../whatsminer/M3X#m30s-ve30-stock">M30S VE30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve30">M30S VE30</a></li>
<li><a href="../whatsminer/M3X#m30s-ve40-stock">M30S VE40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve40">M30S VE40</a></li>
<li><a href="../whatsminer/M3X#m30s-ve50-stock">M30S VE50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve50">M30S VE50</a></li>
<li><a href="../whatsminer/M3X#m30s-ve60-stock">M30S VE60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve60">M30S VE60</a></li>
<li><a href="../whatsminer/M3X#m30s-ve70-stock">M30S VE70 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-ve70">M30S VE70</a></li>
<li><a href="../whatsminer/M3X#m30s-vf10-stock">M30S VF10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vf10">M30S VF10</a></li>
<li><a href="../whatsminer/M3X#m30s-vf20-stock">M30S VF20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vf20">M30S VF20</a></li>
<li><a href="../whatsminer/M3X#m30s-vf30-stock">M30S VF30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vf30">M30S VF30</a></li>
<li><a href="../whatsminer/M3X#m30s-vg10-stock">M30S VG10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vg10">M30S VG10</a></li>
<li><a href="../whatsminer/M3X#m30s-vg20-stock">M30S VG20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vg20">M30S VG20</a></li>
<li><a href="../whatsminer/M3X#m30s-vg30-stock">M30S VG30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vg30">M30S VG30</a></li>
<li><a href="../whatsminer/M3X#m30s-vg40-stock">M30S VG40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vg40">M30S VG40</a></li>
<li><a href="../whatsminer/M3X#m30s-vh10-stock">M30S VH10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vh10">M30S VH10</a></li>
<li><a href="../whatsminer/M3X#m30s-vh20-stock">M30S VH20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vh20">M30S VH20</a></li>
<li><a href="../whatsminer/M3X#m30s-vh30-stock">M30S VH30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vh30">M30S VH30</a></li>
<li><a href="../whatsminer/M3X#m30s-vh40-stock">M30S VH40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vh40">M30S VH40</a></li>
<li><a href="../whatsminer/M3X#m30s-vh50-stock">M30S VH50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vh50">M30S VH50</a></li>
<li><a href="../whatsminer/M3X#m30s-vh60-stock">M30S VH60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vh60">M30S VH60</a></li>
<li><a href="../whatsminer/M3X#m30s-vi20-stock">M30S VI20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s-vi20">M30S VI20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v10-stock">M30S+ V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v10">M30S+ V10</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v20-stock">M30S+ V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v20">M30S+ V20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v30-stock">M30S+ V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v30">M30S+ V30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v40-stock">M30S+ V40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v40">M30S+ V40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v50-stock">M30S+ V50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v50">M30S+ V50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v60-stock">M30S+ V60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v60">M30S+ V60</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v70-stock">M30S+ V70 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v70">M30S+ V70</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v80-stock">M30S+ V80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v80">M30S+ V80</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v90-stock">M30S+ V90 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v90">M30S+ V90</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v100-stock">M30S+ V100 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-v100">M30S+ V100</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve30-stock">M30S+ VE30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve30">M30S+ VE30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve40-stock">M30S+ VE40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve40">M30S+ VE40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve50-stock">M30S+ VE50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve50">M30S+ VE50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve60-stock">M30S+ VE60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve60">M30S+ VE60</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve70-stock">M30S+ VE70 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve70">M30S+ VE70</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve80-stock">M30S+ VE80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve80">M30S+ VE80</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve90-stock">M30S+ VE90 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve90">M30S+ VE90</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve100-stock">M30S+ VE100 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-ve100">M30S+ VE100</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf20-stock">M30S+ VF20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vf20">M30S+ VF20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf30-stock">M30S+ VF30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vf30">M30S+ VF30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg20-stock">M30S+ VG20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vg20">M30S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg30-stock">M30S+ VG30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vg30">M30S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg40-stock">M30S+ VG40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vg40">M30S+ VG40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg50-stock">M30S+ VG50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vg50">M30S+ VG50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg60-stock">M30S+ VG60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vg60">M30S+ VG60</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh10-stock">M30S+ VH10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vh10">M30S+ VH10</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh20-stock">M30S+ VH20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vh20">M30S+ VH20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh30-stock">M30S+ VH30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vh30">M30S+ VH30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh40-stock">M30S+ VH40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vh40">M30S+ VH40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh50-stock">M30S+ VH50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vh50">M30S+ VH50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh60-stock">M30S+ VH60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1-vh60">M30S+ VH60</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-v10-stock">M30S++ V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-v10">M30S++ V10</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-v20-stock">M30S++ V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-v20">M30S++ V20</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve30-stock">M30S++ VE30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-ve30">M30S++ VE30</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve40-stock">M30S++ VE40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-ve40">M30S++ VE40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve50-stock">M30S++ VE50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-ve50">M30S++ VE50</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vf40-stock">M30S++ VF40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vf40">M30S++ VF40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg30-stock">M30S++ VG30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vg30">M30S++ VG30</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg40-stock">M30S++ VG40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vg40">M30S++ VG40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg50-stock">M30S++ VG50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vg50">M30S++ VG50</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh10-stock">M30S++ VH10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh10">M30S++ VH10</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh20-stock">M30S++ VH20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh20">M30S++ VH20</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh30-stock">M30S++ VH30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh30">M30S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh40-stock">M30S++ VH40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh40">M30S++ VH40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh50-stock">M30S++ VH50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh50">M30S++ VH50</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh60-stock">M30S++ VH60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh60">M30S++ VH60</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh70-stock">M30S++ VH70 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh70">M30S++ VH70</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh80-stock">M30S++ VH80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh80">M30S++ VH80</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh90-stock">M30S++ VH90 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh90">M30S++ VH90</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh100-stock">M30S++ VH100 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vh100">M30S++ VH100</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vj20-stock">M30S++ VJ20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vj20">M30S++ VJ20</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vj30-stock">M30S++ VJ30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30s_1_1-vj30">M30S++ VJ30</a></li>
<li><a href="../whatsminer/M3X#m31-v10-stock">M31 V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31-v10">M31 V10</a></li>
<li><a href="../whatsminer/M3X#m31-v20-stock">M31 V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31-v20">M31 V20</a></li>
<li><a href="../whatsminer/M3X#m31h-v10-stock">M31H V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31h-v10">M31H V10</a></li>
<li><a href="../whatsminer/M3X#m31h-v40-stock">M31H V40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31h-v40">M31H V40</a></li>
<li><a href="../whatsminer/M3X#m30l-v10-stock">M30L V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
<li><a href="../whatsminer/M3X#m31s-v10-stock">M31S V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v10">M31S V10</a></li>
<li><a href="../whatsminer/M3X#m31s-v20-stock">M31S V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v20">M31S V20</a></li>
<li><a href="../whatsminer/M3X#m31s-v30-stock">M31S V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v30">M31S V30</a></li>
<li><a href="../whatsminer/M3X#m31s-v40-stock">M31S V40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v40">M31S V40</a></li>
<li><a href="../whatsminer/M3X#m31s-v50-stock">M31S V50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v50">M31S V50</a></li>
<li><a href="../whatsminer/M3X#m31s-v60-stock">M31S V60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v60">M31S V60</a></li>
<li><a href="../whatsminer/M3X#m31s-v70-stock">M31S V70 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v70">M31S V70</a></li>
<li><a href="../whatsminer/M3X#m31s-v80-stock">M31S V80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v80">M31S V80</a></li>
<li><a href="../whatsminer/M3X#m31s-v90-stock">M31S V90 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-v90">M31S V90</a></li>
<li><a href="../whatsminer/M3X#m31s-ve10-stock">M31S VE10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-ve10">M31S VE10</a></li>
<li><a href="../whatsminer/M3X#m31s-ve20-stock">M31S VE20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-ve20">M31S VE20</a></li>
<li><a href="../whatsminer/M3X#m31s-ve30-stock">M31S VE30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s-ve30">M31S VE30</a></li>
<li><a href="../whatsminer/M3X#m31se-v10-stock">M31SE V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31se-v10">M31SE V10</a></li>
<li><a href="../whatsminer/M3X#m31se-v20-stock">M31SE V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31se-v20">M31SE V20</a></li>
<li><a href="../whatsminer/M3X#m31se-v30-stock">M31SE V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31se-v30">M31SE V30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v10-stock">M31S+ V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v10">M31S+ V10</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v20-stock">M31S+ V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v20">M31S+ V20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v30-stock">M31S+ V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v30">M31S+ V30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v40-stock">M31S+ V40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v40">M31S+ V40</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v50-stock">M31S+ V50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v50">M31S+ V50</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v60-stock">M31S+ V60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v60">M31S+ V60</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v80-stock">M31S+ V80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v80">M31S+ V80</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v90-stock">M31S+ V90 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v90">M31S+ V90</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v100-stock">M31S+ V100 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-v100">M31S+ V100</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve10-stock">M31S+ VE10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve10">M31S+ VE10</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve20-stock">M31S+ VE20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve20">M31S+ VE20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve30-stock">M31S+ VE30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve30">M31S+ VE30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve40-stock">M31S+ VE40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve40">M31S+ VE40</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve50-stock">M31S+ VE50 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve50">M31S+ VE50</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve60-stock">M31S+ VE60 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve60">M31S+ VE60</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve80-stock">M31S+ VE80 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-ve80">M31S+ VE80</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vf20-stock">M31S+ VF20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-vf20">M31S+ VF20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vf30-stock">M31S+ VF30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-vf30">M31S+ VF30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vg20-stock">M31S+ VG20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-vg20">M31S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vg30-stock">M31S+ VG30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m31s_1-vg30">M31S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m32-v10-stock">M32 V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m32-v10">M32 V10</a></li>
<li><a href="../whatsminer/M3X#m32-v20-stock">M32 V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m32-v20">M32 V20</a></li>
<li><a href="../whatsminer/M3X#m33-v10-stock">M33 V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33-v10">M33 V10</a></li>
<li><a href="../whatsminer/M3X#m33-v20-stock">M33 V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33-v20">M33 V20</a></li>
<li><a href="../whatsminer/M3X#m33-v30-stock">M33 V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33-v30">M33 V30</a></li>
<li><a href="../whatsminer/M3X#m33s-vg30-stock">M33S VG30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s-vg30">M33S VG30</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vg20-stock">M33S+ VG20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s_1-vg20">M33S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh20-stock">M33S+ VH20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s_1-vh20">M33S+ VH20</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh30-stock">M33S+ VH30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s_1-vh30">M33S+ VH30</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh20-stock">M33S++ VH20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s_1_1-vh20">M33S++ VH20</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh30-stock">M33S++ VH30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s_1_1-vh30">M33S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vg40-stock">M33S++ VG40 (Stock)</a></li> <li><a href="../whatsminer/M3X#m33s_1_1-vg40">M33S++ VG40</a></li>
<li><a href="../whatsminer/M3X#m34s_1-ve10-stock">M34S+ VE10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m34s_1-ve10">M34S+ VE10</a></li>
<li><a href="../whatsminer/M3X#m36s-ve10-stock">M36S VE10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m36s-ve10">M36S VE10</a></li>
<li><a href="../whatsminer/M3X#m36s_1-vg30-stock">M36S+ VG30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m36s_1-vg30">M36S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m36s_1_1-vh30-stock">M36S++ VH30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m36s_1_1-vh30">M36S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m39-v10-stock">M39 V10 (Stock)</a></li> <li><a href="../whatsminer/M3X#m39-v10">M39 V10</a></li>
<li><a href="../whatsminer/M3X#m39-v20-stock">M39 V20 (Stock)</a></li> <li><a href="../whatsminer/M3X#m39-v20">M39 V20</a></li>
<li><a href="../whatsminer/M3X#m39-v30-stock">M39 V30 (Stock)</a></li> <li><a href="../whatsminer/M3X#m39-v30">M39 V30</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>M5X Series:</summary> <summary>M5X Series:</summary>
<ul> <ul>
<li><a href="../whatsminer/M5X#m50-ve30-stock">M50 VE30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-ve30">M50 VE30</a></li>
<li><a href="../whatsminer/M5X#m50-vg30-stock">M50 VG30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vg30">M50 VG30</a></li>
<li><a href="../whatsminer/M5X#m50-vh10-stock">M50 VH10 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh10">M50 VH10</a></li>
<li><a href="../whatsminer/M5X#m50-vh20-stock">M50 VH20 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh20">M50 VH20</a></li>
<li><a href="../whatsminer/M5X#m50-vh30-stock">M50 VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh30">M50 VH30</a></li>
<li><a href="../whatsminer/M5X#m50-vh40-stock">M50 VH40 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh40">M50 VH40</a></li>
<li><a href="../whatsminer/M5X#m50-vh50-stock">M50 VH50 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh50">M50 VH50</a></li>
<li><a href="../whatsminer/M5X#m50-vh60-stock">M50 VH60 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh60">M50 VH60</a></li>
<li><a href="../whatsminer/M5X#m50-vh70-stock">M50 VH70 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh70">M50 VH70</a></li>
<li><a href="../whatsminer/M5X#m50-vh80-stock">M50 VH80 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vh80">M50 VH80</a></li>
<li><a href="../whatsminer/M5X#m50-vh90-stock">M50 VH90 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vj10">M50 VJ10</a></li>
<li><a href="../whatsminer/M5X#m50-vj10-stock">M50 VJ10 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vj20">M50 VJ20</a></li>
<li><a href="../whatsminer/M5X#m50-vj20-stock">M50 VJ20 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50-vj30">M50 VJ30</a></li>
<li><a href="../whatsminer/M5X#m50-vj30-stock">M50 VJ30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vj10">M50S VJ10</a></li>
<li><a href="../whatsminer/M5X#m50s-vj10-stock">M50S VJ10 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vj20">M50S VJ20</a></li>
<li><a href="../whatsminer/M5X#m50s-vj20-stock">M50S VJ20 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vj30">M50S VJ30</a></li>
<li><a href="../whatsminer/M5X#m50s-vj30-stock">M50S VJ30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vh10">M50S VH10</a></li>
<li><a href="../whatsminer/M5X#m50s-vh10-stock">M50S VH10 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vh20">M50S VH20</a></li>
<li><a href="../whatsminer/M5X#m50s-vh20-stock">M50S VH20 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vh30">M50S VH30</a></li>
<li><a href="../whatsminer/M5X#m50s-vh30-stock">M50S VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vh40">M50S VH40</a></li>
<li><a href="../whatsminer/M5X#m50s-vh40-stock">M50S VH40 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s-vh50">M50S VH50</a></li>
<li><a href="../whatsminer/M5X#m50s-vh50-stock">M50S VH50 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1-vh30">M50S+ VH30</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vh30-stock">M50S+ VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1-vh40">M50S+ VH40</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vh40-stock">M50S+ VH40 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1-vj30">M50S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vj30-stock">M50S+ VJ30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1-vk20">M50S+ VK20</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vk20-stock">M50S+ VK20 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1_1-vk10">M50S++ VK10</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk10-stock">M50S++ VK10 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1_1-vk20">M50S++ VK20</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk20-stock">M50S++ VK20 (Stock)</a></li> <li><a href="../whatsminer/M5X#m50s_1_1-vk30">M50S++ VK30</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk30-stock">M50S++ VK30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m53-vh30">M53 VH30</a></li>
<li><a href="../whatsminer/M5X#m53-vh30-stock">M53 VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m53s-vh30">M53S VH30</a></li>
<li><a href="../whatsminer/M5X#m53s-vh30-stock">M53S VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m53s-vj40">M53S VJ40</a></li>
<li><a href="../whatsminer/M5X#m53s-vj40-stock">M53S VJ40 (Stock)</a></li> <li><a href="../whatsminer/M5X#m53s_1-vj30">M53S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m53s_1-vj30-stock">M53S+ VJ30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m53s_1_1-vk10">M53S++ VK10</a></li>
<li><a href="../whatsminer/M5X#m53s_1_1-vk10-stock">M53S++ VK10 (Stock)</a></li> <li><a href="../whatsminer/M5X#m56-vh30">M56 VH30</a></li>
<li><a href="../whatsminer/M5X#m56-vh30-stock">M56 VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m56s-vh30">M56S VH30</a></li>
<li><a href="../whatsminer/M5X#m56s-vh30-stock">M56S VH30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m56s_1-vj30">M56S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m56s_1-vj30-stock">M56S+ VJ30 (Stock)</a></li> <li><a href="../whatsminer/M5X#m59-vh30">M59 VH30</a></li>
<li><a href="../whatsminer/M5X#m59-vh30-stock">M59 VH30 (Stock)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>M6X Series:</summary> <summary>M6X Series:</summary>
<ul> <ul>
<li><a href="../whatsminer/M6X#m60-vk10-stock">M60 VK10 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60-vk10">M60 VK10</a></li>
<li><a href="../whatsminer/M6X#m60-vk20-stock">M60 VK20 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60-vk20">M60 VK20</a></li>
<li><a href="../whatsminer/M6X#m60-vk30-stock">M60 VK30 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60-vk30">M60 VK30</a></li>
<li><a href="../whatsminer/M6X#m60-vk40-stock">M60 VK40 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60-vk40">M60 VK40</a></li>
<li><a href="../whatsminer/M6X#m60s-vk10-stock">M60S VK10 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60s-vk10">M60S VK10</a></li>
<li><a href="../whatsminer/M6X#m60s-vk20-stock">M60S VK20 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60s-vk20">M60S VK20</a></li>
<li><a href="../whatsminer/M6X#m60s-vk30-stock">M60S VK30 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60s-vk30">M60S VK30</a></li>
<li><a href="../whatsminer/M6X#m60s-vk40-stock">M60S VK40 (Stock)</a></li> <li><a href="../whatsminer/M6X#m60s-vk40">M60S VK40</a></li>
<li><a href="../whatsminer/M6X#m63-vk10-stock">M63 VK10 (Stock)</a></li> <li><a href="../whatsminer/M6X#m63-vk10">M63 VK10</a></li>
<li><a href="../whatsminer/M6X#m63-vk20-stock">M63 VK20 (Stock)</a></li> <li><a href="../whatsminer/M6X#m63-vk20">M63 VK20</a></li>
<li><a href="../whatsminer/M6X#m63-vk30-stock">M63 VK30 (Stock)</a></li> <li><a href="../whatsminer/M6X#m63-vk30">M63 VK30</a></li>
<li><a href="../whatsminer/M6X#m63s-vk10-stock">M63S VK10 (Stock)</a></li> <li><a href="../whatsminer/M6X#m63s-vk10">M63S VK10</a></li>
<li><a href="../whatsminer/M6X#m63s-vk20-stock">M63S VK20 (Stock)</a></li> <li><a href="../whatsminer/M6X#m63s-vk20">M63S VK20</a></li>
<li><a href="../whatsminer/M6X#m63s-vk30-stock">M63S VK30 (Stock)</a></li> <li><a href="../whatsminer/M6X#m63s-vk30">M63S VK30</a></li>
<li><a href="../whatsminer/M6X#m66-vk20-stock">M66 VK20 (Stock)</a></li> <li><a href="../whatsminer/M6X#m66-vk20">M66 VK20</a></li>
<li><a href="../whatsminer/M6X#m66-vk30-stock">M66 VK30 (Stock)</a></li> <li><a href="../whatsminer/M6X#m66-vk30">M66 VK30</a></li>
<li><a href="../whatsminer/M6X#m66s-vk20-stock">M66S VK20 (Stock)</a></li> <li><a href="../whatsminer/M6X#m66s-vk20">M66S VK20</a></li>
<li><a href="../whatsminer/M6X#m66s-vk30-stock">M66S VK30 (Stock)</a></li> <li><a href="../whatsminer/M6X#m66s-vk30">M66S VK30</a></li>
<li><a href="../whatsminer/M6X#m66s-vk40-stock">M66S VK40 (Stock)</a></li> <li><a href="../whatsminer/M6X#m66s-vk40">M66S VK40</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -347,49 +338,43 @@ details {
<details> <details>
<summary>A7X Series:</summary> <summary>A7X Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/A7X#avalon-721-stock">Avalon 721 (Stock)</a></li> <li><a href="../avalonminer/A7X#avalon-721">Avalon 721</a></li>
<li><a href="../avalonminer/A7X#avalon-741-stock">Avalon 741 (Stock)</a></li> <li><a href="../avalonminer/A7X#avalon-741">Avalon 741</a></li>
<li><a href="../avalonminer/A7X#avalon-761-stock">Avalon 761 (Stock)</a></li> <li><a href="../avalonminer/A7X#avalon-761">Avalon 761</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>A8X Series:</summary> <summary>A8X Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/A8X#avalon-821-stock">Avalon 821 (Stock)</a></li> <li><a href="../avalonminer/A8X#avalon-821">Avalon 821</a></li>
<li><a href="../avalonminer/A8X#avalon-841-stock">Avalon 841 (Stock)</a></li> <li><a href="../avalonminer/A8X#avalon-841">Avalon 841</a></li>
<li><a href="../avalonminer/A8X#avalon-851-stock">Avalon 851 (Stock)</a></li> <li><a href="../avalonminer/A8X#avalon-851">Avalon 851</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>A9X Series:</summary> <summary>A9X Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/A9X#avalon-921-stock">Avalon 921 (Stock)</a></li> <li><a href="../avalonminer/A9X#avalon-921">Avalon 921</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>A10X Series:</summary> <summary>A10X Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/A10X#avalon-1026-stock">Avalon 1026 (Stock)</a></li> <li><a href="../avalonminer/A10X#avalon-1026">Avalon 1026</a></li>
<li><a href="../avalonminer/A10X#avalon-1047-stock">Avalon 1047 (Stock)</a></li> <li><a href="../avalonminer/A10X#avalon-1047">Avalon 1047</a></li>
<li><a href="../avalonminer/A10X#avalon-1066-stock">Avalon 1066 (Stock)</a></li> <li><a href="../avalonminer/A10X#avalon-1066">Avalon 1066</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>A11X Series:</summary> <summary>A11X Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/A11X#avalon-1166-pro-stock">Avalon 1166 Pro (Stock)</a></li> <li><a href="../avalonminer/A11X#avalon-1166-pro">Avalon 1166 Pro</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>A12X Series:</summary> <summary>A12X Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/A12X#avalon-1246-stock">Avalon 1246 (Stock)</a></li> <li><a href="../avalonminer/A12X#avalon-1246">Avalon 1246</a></li>
</ul>
</details>
<details>
<summary>nano Series:</summary>
<ul>
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -400,20 +385,13 @@ details {
<details> <details>
<summary>T3X Series:</summary> <summary>T3X Series:</summary>
<ul> <ul>
<li><a href="../innosilicon/T3X#t3h_1-stock">T3H+ (Stock)</a></li> <li><a href="../innosilicon/T3X#t3h_1">T3H+</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>A10X Series:</summary> <summary>A10X Series:</summary>
<ul> <ul>
<li><a href="../innosilicon/A10X#a10x-stock">A10X (Stock)</a></li> <li><a href="../innosilicon/A10X#a10x">A10X</a></li>
</ul>
</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> </ul>
</details> </details>
</ul> </ul>
@@ -424,22 +402,22 @@ details {
<details> <details>
<summary>X5 Series:</summary> <summary>X5 Series:</summary>
<ul> <ul>
<li><a href="../goldshell/X5#ck5-stock">CK5 (Stock)</a></li> <li><a href="../goldshell/X5#ck5">CK5</a></li>
<li><a href="../goldshell/X5#hs5-stock">HS5 (Stock)</a></li> <li><a href="../goldshell/X5#hs5">HS5</a></li>
<li><a href="../goldshell/X5#kd5-stock">KD5 (Stock)</a></li> <li><a href="../goldshell/X5#kd5">KD5</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>XMax Series:</summary> <summary>XMax Series:</summary>
<ul> <ul>
<li><a href="../goldshell/XMax#kd-max-stock">KD Max (Stock)</a></li> <li><a href="../goldshell/XMax#kd-max">KD Max</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>XBox Series:</summary> <summary>XBox Series:</summary>
<ul> <ul>
<li><a href="../goldshell/XBox#kd-box-ii-stock">KD Box II (Stock)</a></li> <li><a href="../goldshell/XBox#kd-box-ii">KD Box II</a></li>
<li><a href="../goldshell/XBox#kd-box-pro-stock">KD Box Pro (Stock)</a></li> <li><a href="../goldshell/XBox#kd-box-pro">KD Box Pro</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -468,30 +446,27 @@ details {
<details> <details>
<summary>X19 Series:</summary> <summary>X19 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X19#s19-bos_1">S19 (BOS+)</a></li> <li><a href="../antminer/X19#s19">S19</a></li>
<li><a href="../antminer/X19#s19_1-bos_1">S19+ (BOS+)</a></li> <li><a href="../antminer/X19#s19_1">S19+</a></li>
<li><a href="../antminer/X19#s19-pro-bos_1">S19 Pro (BOS+)</a></li> <li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
<li><a href="../antminer/X19#s19a-bos_1">S19a (BOS+)</a></li> <li><a href="../antminer/X19#s19a">S19a</a></li>
<li><a href="../antminer/X19#s19a-pro-bos_1">S19a Pro (BOS+)</a></li> <li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li>
<li><a href="../antminer/X19#s19j-bos_1">S19j (BOS+)</a></li> <li><a href="../antminer/X19#s19j">S19j</a></li>
<li><a href="../antminer/X19#s19j-no-pic-bos_1">S19j No PIC (BOS+)</a></li> <li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
<li><a href="../antminer/X19#s19j-pro-bos_1">S19j Pro (BOS+)</a></li> <li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
<li><a href="../antminer/X19#s19j-pro-no-pic-bos_1">S19j Pro No PIC (BOS+)</a></li> <li><a href="../antminer/X19#s19j-pro-no-pic">S19j Pro No PIC</a></li>
<li><a href="../antminer/X19#s19j-pro_1-bos_1">S19j Pro+ (BOS+)</a></li> <li><a href="../antminer/X19#s19j-pro_1">S19j Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro_1-bos_1">S19j Pro+ (BOS+)</a></li> <li><a href="../antminer/X19#s19j-pro_1">S19j Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro_1-no-pic-bos_1">S19j Pro+ No PIC (BOS+)</a></li> <li><a href="../antminer/X19#s19j-pro_1-no-pic">S19j Pro+ No PIC</a></li>
<li><a href="../antminer/X19#s19k-pro-no-pic-bos_1">S19k Pro No PIC (BOS+)</a></li> <li><a href="../antminer/X19#s19k-pro-no-pic">S19k Pro No PIC</a></li>
<li><a href="../antminer/X19#s19k-pro-no-pic-bos_1">S19k Pro No PIC (BOS+)</a></li> <li><a href="../antminer/X19#s19-xp">S19 XP</a></li>
<li><a href="../antminer/X19#s19-xp-bos_1">S19 XP (BOS+)</a></li> <li><a href="../antminer/X19#t19">T19</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro-bos_1">S19 Pro+ Hydro (BOS+)</a></li>
<li><a href="../antminer/X19#t19-bos_1">T19 (BOS+)</a></li>
</ul> </ul>
</details> </details>
<details> <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">S21</a></li>
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -527,19 +502,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#t19-vnish">T19 (VNish)</a></li> <li><a href="../antminer/X19#t19-vnish">T19 (VNish)</a></li>
</ul> </ul>
</details> </details>
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-vnish">S21 (VNish)</a></li>
</ul>
</details>
</ul> </ul>
</details> </details>
<details> <details>
@@ -561,7 +528,6 @@ details {
<summary>X21 Series:</summary> <summary>X21 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X21#s21-epic">S21 (ePIC)</a></li> <li><a href="../antminer/X21#s21-epic">S21 (ePIC)</a></li>
<li><a href="../antminer/X21#s21-pro-epic">S21 Pro (ePIC)</a></li>
<li><a href="../antminer/X21#t21-epic">T21 (ePIC)</a></li> <li><a href="../antminer/X21#t21-epic">T21 (ePIC)</a></li>
</ul> </ul>
</details> </details>
@@ -580,13 +546,7 @@ 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-stock">S19j Pro (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -626,23 +586,23 @@ details {
<details> <details>
<summary>AD Series:</summary> <summary>AD Series:</summary>
<ul> <ul>
<li><a href="../auradine/AD#at1500-stock">AT1500 (Stock)</a></li> <li><a href="../auradine/AD#at1500">AT1500</a></li>
<li><a href="../auradine/AD#at2860-stock">AT2860 (Stock)</a></li> <li><a href="../auradine/AD#at2860">AT2860</a></li>
<li><a href="../auradine/AD#at2880-stock">AT2880 (Stock)</a></li> <li><a href="../auradine/AD#at2880">AT2880</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>AI Series:</summary> <summary>AI Series:</summary>
<ul> <ul>
<li><a href="../auradine/AI#ai2500-stock">AI2500 (Stock)</a></li> <li><a href="../auradine/AI#ai2500">AI2500</a></li>
<li><a href="../auradine/AI#ai3680-stock">AI3680 (Stock)</a></li> <li><a href="../auradine/AI#ai3680">AI3680</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>AT Series:</summary> <summary>AT Series:</summary>
<ul> <ul>
<li><a href="../auradine/AT#ad2500-stock">AD2500 (Stock)</a></li> <li><a href="../auradine/AT#ad2500">AD2500</a></li>
<li><a href="../auradine/AT#ad3500-stock">AD3500 (Stock)</a></li> <li><a href="../auradine/AT#ad3500">AD3500</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -671,47 +631,3 @@ details {
</details> </details>
</ul> </ul>
</details> </details>
<details>
<summary>Stock Firmware BitAxe Miners:</summary>
<ul>
<details>
<summary>BM Series:</summary>
<ul>
<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#max-stock">Max (Stock)</a></li>
<li><a href="../bitaxe/BM#gamma-stock">Gamma (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware IceRiver Miners:</summary>
<ul>
<details>
<summary>KSX Series:</summary>
<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#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>
</details>
</ul>
</details>

View File

@@ -1,91 +1,91 @@
# pyasic # pyasic
## M2X Models ## M2X Models
## M20 V10 (Stock) ## M20 V10
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10 ::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M20S V10 (Stock) ## M20S V10
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10 ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M20S V20 (Stock) ## M20S V20
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20 ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M20S V30 (Stock) ## M20S V30
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30 ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M20P V10 (Stock) ## M20P V10
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10 ::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M20P V30 (Stock) ## M20P V30
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30 ::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M20S+ V30 (Stock) ## M20S+ V30
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30 ::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M21 V10 (Stock) ## M21 V10
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10 ::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M21S V20 (Stock) ## M21S V20
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20 ::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M21S V60 (Stock) ## M21S V60
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60 ::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M21S V70 (Stock) ## M21S V70
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70 ::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M21S+ V20 (Stock) ## M21S+ V20
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20 ::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M29 V10 (Stock) ## M29 V10
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10 ::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
handler: python handler: python
options: options:

File diff suppressed because it is too large Load Diff

View File

@@ -1,266 +1,259 @@
# pyasic # pyasic
## M5X Models ## M5X Models
## M50 VE30 (Stock) ## M50 VE30
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VG30 (Stock) ## M50 VG30
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH10 (Stock) ## M50 VH10
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH10 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH20 (Stock) ## M50 VH20
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH20 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH30 (Stock) ## M50 VH30
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH30 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH40 (Stock) ## M50 VH40
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH40 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH40
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH50 (Stock) ## M50 VH50
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH50 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH50
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH60 (Stock) ## M50 VH60
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH60 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH60
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH70 (Stock) ## M50 VH70
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH70 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH70
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH80 (Stock) ## M50 VH80
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH80 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH80
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VH90 (Stock) ## M50 VJ10
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH90
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VJ10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VJ20 (Stock) ## M50 VJ20
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ20 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50 VJ30 (Stock) ## M50 VJ30
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ30 ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VJ10 (Stock) ## M50S VJ10
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ10 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VJ20 (Stock) ## M50S VJ20
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ20 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VJ30 (Stock) ## M50S VJ30
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ30 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VH10 (Stock) ## M50S VH10
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH10 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VH20 (Stock) ## M50S VH20
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH20 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VH30 (Stock) ## M50S VH30
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH30 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VH40 (Stock) ## M50S VH40
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH40 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH40
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S VH50 (Stock) ## M50S VH50
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH50 ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH50
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S+ VH30 (Stock) ## M50S+ VH30
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH30 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S+ VH40 (Stock) ## M50S+ VH40
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH40 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH40
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S+ VJ30 (Stock) ## M50S+ VJ30
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ30 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S+ VK20 (Stock) ## M50S+ VK20
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK20 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S++ VK10 (Stock) ## M50S++ VK10
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK10 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S++ VK20 (Stock) ## M50S++ VK20
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK20 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M50S++ VK30 (Stock) ## M50S++ VK30
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK30 ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53 VH30 (Stock) ## M53 VH30
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH30 ::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53S VH30 (Stock) ## M53S VH30
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH30 ::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53S VJ40 (Stock) ## M53S VJ40
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ40 ::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ40
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53S+ VJ30 (Stock) ## M53S+ VJ30
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30 ::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53S++ VK10 (Stock) ## M53S++ VK10
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK10 ::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M56 VH30 (Stock) ## M56 VH30
::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30 ::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M56S VH30 (Stock) ## M56S VH30
::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVH30 ::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVH30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M56S+ VJ30 (Stock) ## M56S+ VJ30
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVJ30 ::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVJ30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M59 VH30 (Stock) ## M59 VH30
::: pyasic.miners.whatsminer.btminer.M5X.M59.BTMinerM59VH30 ::: pyasic.miners.whatsminer.btminer.M5X.M59.BTMinerM59VH30
handler: python handler: python
options: options:

View File

@@ -1,133 +1,133 @@
# pyasic # pyasic
## M6X Models ## M6X Models
## M60 VK10 (Stock) ## M60 VK10
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK10 ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60 VK20 (Stock) ## M60 VK20
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK20 ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60 VK30 (Stock) ## M60 VK30
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK30 ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60 VK40 (Stock) ## M60 VK40
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK40 ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK40
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60S VK10 (Stock) ## M60S VK10
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK10 ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60S VK20 (Stock) ## M60S VK20
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK20 ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60S VK30 (Stock) ## M60S VK30
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK30 ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M60S VK40 (Stock) ## M60S VK40
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK40 ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK40
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M63 VK10 (Stock) ## M63 VK10
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK10 ::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M63 VK20 (Stock) ## M63 VK20
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK20 ::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M63 VK30 (Stock) ## M63 VK30
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK30 ::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M63S VK10 (Stock) ## M63S VK10
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK10 ::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK10
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M63S VK20 (Stock) ## M63S VK20
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK20 ::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M63S VK30 (Stock) ## M63S VK30
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK30 ::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M66 VK20 (Stock) ## M66 VK20
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK20 ::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M66 VK30 (Stock) ## M66 VK30
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK30 ::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M66S VK20 (Stock) ## M66S VK20
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK20 ::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK20
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M66S VK30 (Stock) ## M66S VK30
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK30 ::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK30
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M66S VK40 (Stock) ## M66S VK40
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK40 ::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK40
handler: python handler: python
options: options:

View File

@@ -1,4 +1,3 @@
jinja2<3.1.4 jinja2<3.1.3
mkdocs mkdocs
mkdocstrings[python] mkdocstrings[python]
zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability

View File

@@ -13,17 +13,13 @@ Settings options:
- `get_data_retries` - `get_data_retries`
- `api_function_timeout` - `api_function_timeout`
- `antminer_mining_mode_as_str` - `antminer_mining_mode_as_str`
- `default_whatsminer_rpc_password` - `default_whatsminer_password`
- `default_innosilicon_web_password` - `default_innosilicon_password`
- `default_antminer_web_password` - `default_antminer_password`
- `default_bosminer_web_password` - `default_bosminer_password`
- `default_vnish_web_password` - `default_vnish_password`
- `default_goldshell_web_password` - `default_goldshell_password`
- `default_auradine_web_password` - `socket_linger_time`
- `default_epic_web_password`
- `default_hive_web_password`
- `default_antminer_ssh_password`
- `default_bosminer_ssh_password`
### get ### get

View File

@@ -68,8 +68,6 @@ nav:
- Auradine AD: "miners/auradine/AD.md" - Auradine AD: "miners/auradine/AD.md"
- Auradine AI: "miners/auradine/AI.md" - Auradine AI: "miners/auradine/AI.md"
- Auradine AT: "miners/auradine/AT.md" - Auradine AT: "miners/auradine/AT.md"
- Blockminer: "miners/blockminer/blockminer.md"
- BitAxe BM: "miners/bitaxe/BM.md"
- Base Miner: "miners/base_miner.md" - Base Miner: "miners/base_miner.md"
- Settings: - Settings:
- Settings: "settings/settings.md" - Settings: "settings/settings.md"

1321
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,38 +13,31 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from dataclasses import asdict, dataclass, field
from pydantic import BaseModel, Field from pyasic.config.fans import FanModeConfig
from pyasic.config.mining import MiningModeConfig
from pyasic.config.fans import FanMode, FanModeConfig, FanModeNormal
from pyasic.config.mining import MiningMode, MiningModeConfig
from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig from pyasic.config.pools import PoolConfig
from pyasic.config.power_scaling import PowerScalingConfig
from pyasic.config.temperature import TemperatureConfig from pyasic.config.temperature import TemperatureConfig
from pyasic.misc import merge_dicts from pyasic.misc import merge_dicts
class MinerConfig(BaseModel): @dataclass
class MinerConfig:
"""Represents the configuration for a miner including pool configuration, """Represents the configuration for a miner including pool configuration,
fan mode, temperature settings, mining mode, and power scaling.""" fan mode, temperature settings, mining mode, and power scaling."""
pools: PoolConfig = field(default_factory=PoolConfig.default)
class Config: fan_mode: FanModeConfig = field(default_factory=FanModeConfig.default)
arbitrary_types_allowed = True temperature: TemperatureConfig = field(default_factory=TemperatureConfig.default)
mining_mode: MiningModeConfig = field(default_factory=MiningModeConfig.default)
pools: PoolConfig = Field(default_factory=PoolConfig.default) power_scaling: PowerScalingConfig = field(
fan_mode: FanMode = Field(default_factory=FanModeConfig.default) default_factory=PowerScalingConfig.default
temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default) )
mining_mode: MiningMode = Field(default_factory=MiningModeConfig.default)
def __getitem__(self, item):
try:
return getattr(self, item)
except AttributeError:
raise KeyError
def as_dict(self) -> dict: def as_dict(self) -> dict:
"""Converts the MinerConfig object to a dictionary.""" """Converts the MinerConfig object to a dictionary."""
return self.model_dump() return asdict(self)
def as_am_modern(self, user_suffix: str = None) -> 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."""
@@ -54,6 +47,7 @@ class MinerConfig(BaseModel):
**self.mining_mode.as_am_modern(), **self.mining_mode.as_am_modern(),
**self.pools.as_am_modern(user_suffix=user_suffix), **self.pools.as_am_modern(user_suffix=user_suffix),
**self.temperature.as_am_modern(), **self.temperature.as_am_modern(),
**self.power_scaling.as_am_modern(),
} }
def as_wm(self, user_suffix: str = None) -> dict: def as_wm(self, user_suffix: str = None) -> dict:
@@ -63,6 +57,7 @@ class MinerConfig(BaseModel):
**self.mining_mode.as_wm(), **self.mining_mode.as_wm(),
**self.pools.as_wm(user_suffix=user_suffix), **self.pools.as_wm(user_suffix=user_suffix),
**self.temperature.as_wm(), **self.temperature.as_wm(),
**self.power_scaling.as_wm(),
} }
def as_am_old(self, user_suffix: str = None) -> dict: def as_am_old(self, user_suffix: str = None) -> dict:
@@ -72,6 +67,7 @@ class MinerConfig(BaseModel):
**self.mining_mode.as_am_old(), **self.mining_mode.as_am_old(),
**self.pools.as_am_old(user_suffix=user_suffix), **self.pools.as_am_old(user_suffix=user_suffix),
**self.temperature.as_am_old(), **self.temperature.as_am_old(),
**self.power_scaling.as_am_old(),
} }
def as_goldshell(self, user_suffix: str = None) -> dict: def as_goldshell(self, user_suffix: str = None) -> dict:
@@ -81,6 +77,7 @@ class MinerConfig(BaseModel):
**self.mining_mode.as_goldshell(), **self.mining_mode.as_goldshell(),
**self.pools.as_goldshell(user_suffix=user_suffix), **self.pools.as_goldshell(user_suffix=user_suffix),
**self.temperature.as_goldshell(), **self.temperature.as_goldshell(),
**self.power_scaling.as_goldshell(),
} }
def as_avalon(self, user_suffix: str = None) -> dict: def as_avalon(self, user_suffix: str = None) -> dict:
@@ -90,6 +87,7 @@ class MinerConfig(BaseModel):
**self.mining_mode.as_avalon(), **self.mining_mode.as_avalon(),
**self.pools.as_avalon(user_suffix=user_suffix), **self.pools.as_avalon(user_suffix=user_suffix),
**self.temperature.as_avalon(), **self.temperature.as_avalon(),
**self.power_scaling.as_avalon(),
} }
def as_inno(self, user_suffix: str = None) -> dict: def as_inno(self, user_suffix: str = None) -> dict:
@@ -99,6 +97,7 @@ class MinerConfig(BaseModel):
**self.mining_mode.as_inno(), **self.mining_mode.as_inno(),
**self.pools.as_inno(user_suffix=user_suffix), **self.pools.as_inno(user_suffix=user_suffix),
**self.temperature.as_inno(), **self.temperature.as_inno(),
**self.power_scaling.as_inno(),
} }
def as_bosminer(self, user_suffix: str = None) -> dict: def as_bosminer(self, user_suffix: str = None) -> dict:
@@ -107,15 +106,17 @@ class MinerConfig(BaseModel):
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()), **merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
**self.mining_mode.as_bosminer(), **self.mining_mode.as_bosminer(),
**self.pools.as_bosminer(user_suffix=user_suffix), **self.pools.as_bosminer(user_suffix=user_suffix),
**self.power_scaling.as_bosminer(),
} }
def as_boser(self, user_suffix: str = None) -> dict: def as_boser(self, user_suffix: str = None) -> dict:
"""Generates the configuration in the format suitable for BOSer.""" """"Generates the configuration in the format suitable for BOSer."""
return { return {
**self.fan_mode.as_boser(), **self.fan_mode.as_boser(),
**self.temperature.as_boser(), **self.temperature.as_boser(),
**self.mining_mode.as_boser(), **self.mining_mode.as_boser(),
**self.pools.as_boser(user_suffix=user_suffix), **self.pools.as_boser(user_suffix=user_suffix),
**self.power_scaling.as_boser(),
} }
def as_epic(self, user_suffix: str = None) -> dict: def as_epic(self, user_suffix: str = None) -> dict:
@@ -124,6 +125,7 @@ class MinerConfig(BaseModel):
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()), **merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
**self.mining_mode.as_epic(), **self.mining_mode.as_epic(),
**self.pools.as_epic(user_suffix=user_suffix), **self.pools.as_epic(user_suffix=user_suffix),
**self.power_scaling.as_epic(),
} }
def as_auradine(self, user_suffix: str = None) -> dict: def as_auradine(self, user_suffix: str = None) -> dict:
@@ -133,48 +135,9 @@ class MinerConfig(BaseModel):
**self.temperature.as_auradine(), **self.temperature.as_auradine(),
**self.mining_mode.as_auradine(), **self.mining_mode.as_auradine(),
**self.pools.as_auradine(user_suffix=user_suffix), **self.pools.as_auradine(user_suffix=user_suffix),
**self.power_scaling.as_auradine(),
} }
def as_mara(self, user_suffix: str = None) -> dict:
return {
**self.fan_mode.as_mara(),
**self.temperature.as_mara(),
**self.mining_mode.as_mara(),
**self.pools.as_mara(user_suffix=user_suffix),
}
def as_bitaxe(self, user_suffix: str = None) -> dict:
return {
**self.fan_mode.as_bitaxe(),
**self.temperature.as_bitaxe(),
**self.mining_mode.as_bitaxe(),
**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."""
@@ -183,6 +146,7 @@ class MinerConfig(BaseModel):
mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")), mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")), fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")), temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
power_scaling=PowerScalingConfig.from_dict(dict_conf.get("power_scaling")),
) )
@classmethod @classmethod
@@ -222,6 +186,7 @@ class MinerConfig(BaseModel):
mining_mode=MiningModeConfig.from_bosminer(toml_conf), mining_mode=MiningModeConfig.from_bosminer(toml_conf),
fan_mode=FanModeConfig.from_bosminer(toml_conf), fan_mode=FanModeConfig.from_bosminer(toml_conf),
temperature=TemperatureConfig.from_bosminer(toml_conf), temperature=TemperatureConfig.from_bosminer(toml_conf),
power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
) )
@classmethod @classmethod
@@ -232,6 +197,7 @@ class MinerConfig(BaseModel):
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf), mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
fan_mode=FanModeConfig.from_boser(grpc_miner_conf), fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
temperature=TemperatureConfig.from_boser(grpc_miner_conf), temperature=TemperatureConfig.from_boser(grpc_miner_conf),
power_scaling=PowerScalingConfig.from_boser(grpc_miner_conf),
) )
@classmethod @classmethod
@@ -262,40 +228,3 @@ class MinerConfig(BaseModel):
fan_mode=FanModeConfig.from_auradine(web_conf["fan"]), fan_mode=FanModeConfig.from_auradine(web_conf["fan"]),
mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]), mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]),
) )
@classmethod
def from_mara(cls, web_miner_config: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_mara(web_miner_config),
fan_mode=FanModeConfig.from_mara(web_miner_config),
mining_mode=MiningModeConfig.from_mara(web_miner_config),
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.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)

View File

@@ -15,10 +15,9 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations from __future__ import annotations
from dataclasses import asdict, dataclass
from enum import Enum from enum import Enum
from pydantic import BaseModel
class MinerConfigOption(Enum): class MinerConfigOption(Enum):
@classmethod @classmethod
@@ -47,7 +46,7 @@ class MinerConfigOption(Enum):
return self.value.as_bosminer() return self.value.as_bosminer()
def as_boser(self) -> dict: def as_boser(self) -> dict:
return self.value.as_boser return self.value.as_boser()
def as_epic(self) -> dict: def as_epic(self) -> dict:
return self.value.as_epic() return self.value.as_epic()
@@ -58,15 +57,6 @@ class MinerConfigOption(Enum):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return self.value.as_auradine() return self.value.as_auradine()
def as_mara(self) -> dict:
return self.value.as_mara()
def as_bitaxe(self) -> dict:
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)
@@ -74,20 +64,15 @@ class MinerConfigOption(Enum):
def default(cls): def default(cls):
pass pass
def __getitem__(self, item):
try:
return getattr(self, item)
except AttributeError:
raise KeyError
@dataclass
class MinerConfigValue(BaseModel): class MinerConfigValue:
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None): def from_dict(cls, dict_conf: dict | None):
return cls() return cls()
def as_dict(self) -> dict: def as_dict(self) -> dict:
return self.model_dump() return asdict(self)
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {} return {}
@@ -121,18 +106,3 @@ class MinerConfigValue(BaseModel):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {} return {}
def as_mara(self) -> dict:
return {}
def as_bitaxe(self) -> dict:
return {}
def as_luxos(self) -> dict:
return {}
def __getitem__(self, item):
try:
return getattr(self, item)
except AttributeError:
raise KeyError

View File

@@ -15,15 +15,14 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations from __future__ import annotations
from typing import TypeVar, Union from dataclasses import dataclass, field
from pydantic import Field
from pyasic.config.base import MinerConfigOption, MinerConfigValue from pyasic.config.base import MinerConfigOption, MinerConfigValue
@dataclass
class FanModeNormal(MinerConfigValue): class FanModeNormal(MinerConfigValue):
mode: str = Field(init=False, default="normal") mode: str = field(init=False, default="normal")
minimum_fans: int = 1 minimum_fans: int = 1
minimum_speed: int = 0 minimum_speed: int = 0
@@ -72,36 +71,10 @@ class FanModeNormal(MinerConfigValue):
} }
} }
def as_mara(self) -> dict:
return {
"general-config": {"environment-profile": "AirCooling"},
"advance-config": {
"override-fan-control": False,
"fan-fixed-percent": 0,
},
}
def as_bitaxe(self) -> dict:
return {"autoFanspeed": 1}
def as_luxos(self) -> dict:
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
def as_vnish(self) -> dict:
return {
"cooling": {
"fan_min_count": self.minimum_fans,
"fan_min_duty": self.minimum_speed,
"mode": {
"name": "auto",
"param": None, # Target temp, must be set later...
},
}
}
@dataclass
class FanModeManual(MinerConfigValue): class FanModeManual(MinerConfigValue):
mode: str = Field(init=False, default="manual") mode: str = field(init=False, default="manual")
speed: int = 100 speed: int = 100
minimum_fans: int = 1 minimum_fans: int = 1
@@ -147,36 +120,10 @@ class FanModeManual(MinerConfigValue):
def as_epic(self) -> dict: def as_epic(self) -> dict:
return {"fans": {"Manual": {"speed": self.speed}}} return {"fans": {"Manual": {"speed": self.speed}}}
def as_mara(self) -> dict:
return {
"general-config": {"environment-profile": "AirCooling"},
"advance-config": {
"override-fan-control": True,
"fan-fixed-percent": self.speed,
},
}
def as_bitaxe(self) -> dict:
return {"autoFanspeed": 0, "fanspeed": self.speed}
def as_luxos(self) -> dict:
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
def as_vnish(self) -> dict:
return {
"cooling": {
"fan_min_count": self.minimum_fans,
"fan_min_duty": self.speed,
"mode": {
"name": "manual",
"param": self.speed, # Speed value
},
}
}
@dataclass
class FanModeImmersion(MinerConfigValue): class FanModeImmersion(MinerConfigValue):
mode: str = Field(init=False, default="immersion") mode: str = field(init=False, default="immersion")
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "FanModeImmersion": def from_dict(cls, dict_conf: dict | None) -> "FanModeImmersion":
@@ -193,15 +140,6 @@ class FanModeImmersion(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"fan": {"percentage": 0}} return {"fan": {"percentage": 0}}
def as_mara(self) -> dict:
return {"general-config": {"environment-profile": "OilImmersionCooling"}}
def as_luxos(self) -> dict:
return {"fanset": {"speed": 0, "min_fans": 0}}
def as_vnish(self) -> dict:
return {"cooling": {"mode": {"name": "immers"}}}
class FanModeConfig(MinerConfigOption): class FanModeConfig(MinerConfigOption):
normal = FanModeNormal normal = FanModeNormal
@@ -299,7 +237,7 @@ class FanModeConfig(MinerConfigOption):
keys = temperature_conf.keys() keys = temperature_conf.keys()
if "auto" in keys: if "auto" in keys:
if "minimumRequiredFans" in keys: if "minimumRequiredFans" in keys:
return cls.normal(minimum_fans=temperature_conf["minimumRequiredFans"]) return cls.normal(temperature_conf["minimumRequiredFans"])
return cls.normal() return cls.normal()
if "manual" in keys: if "manual" in keys:
conf = {} conf = {}
@@ -317,53 +255,4 @@ class FanModeConfig(MinerConfigOption):
fan_1_target = fan_data["Target"] fan_1_target = fan_data["Target"]
return cls.manual(speed=round((fan_1_target / fan_1_max) * 100)) return cls.manual(speed=round((fan_1_target / fan_1_max) * 100))
except LookupError: except LookupError:
pass
return cls.default() return cls.default()
@classmethod
def from_mara(cls, web_config: dict):
try:
mode = web_config["general-config"]["environment-profile"]
if mode == "AirCooling":
if web_config["advance-config"]["override-fan-control"]:
return cls.manual(
speed=web_config["advance-config"]["fan-fixed-percent"]
)
return cls.normal()
return cls.immersion()
except LookupError:
pass
return cls.default()
@classmethod
def from_bitaxe(cls, web_system_info: dict):
if web_system_info["autofanspeed"] == 1:
return cls.normal()
else:
return cls.manual(speed=web_system_info["fanspeed"])
@classmethod
def from_luxos(cls, rpc_fans: dict, rpc_tempctrl: dict):
try:
mode = rpc_tempctrl["TEMPCTRL"][0]["Mode"]
if mode == "Manual":
speed = rpc_fans["FANS"][0]["Speed"]
min_fans = rpc_fans["FANCTRL"][0]["MinFans"]
if min_fans == 0 and speed == 0:
return cls.immersion()
return cls.manual(
speed=speed,
minimum_fans=min_fans,
)
return cls.normal(
minimum_fans=rpc_fans["FANCTRL"][0]["MinFans"],
)
except LookupError:
pass
return cls.default()
FanMode = TypeVar(
"FanMode",
bound=Union[FanModeNormal, FanModeManual, FanModeImmersion],
)

View File

@@ -15,30 +15,23 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations from __future__ import annotations
from dataclasses import field from dataclasses import dataclass, 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
from pyasic.web.braiins_os.proto.braiins.bos.v1 import ( from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
DpsHashrateTarget,
DpsPowerTarget,
DpsTarget,
HashrateTargetMode, HashrateTargetMode,
PerformanceMode, PerformanceMode,
Power, Power,
PowerTargetMode, PowerTargetMode,
SaveAction, SaveAction,
SetDpsRequest,
SetPerformanceModeRequest, SetPerformanceModeRequest,
TeraHashrate, TeraHashrate,
TunerPerformanceMode, TunerPerformanceMode,
) )
from .algo import TunerAlgo, TunerAlgoType
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")
@@ -63,17 +56,8 @@ class MiningModeNormal(MinerConfigValue):
def as_goldshell(self) -> dict: def as_goldshell(self) -> dict:
return {"settings": {"level": 0}} return {"settings": {"level": 0}}
def as_mara(self) -> dict:
return {
"mode": {
"work-mode-selector": "Stock",
}
}
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")
@@ -98,14 +82,8 @@ class MiningModeSleep(MinerConfigValue):
def as_goldshell(self) -> dict: def as_goldshell(self) -> dict:
return {"settings": {"level": 3}} return {"settings": {"level": 3}}
def as_mara(self) -> dict:
return {
"mode": {
"work-mode-selector": "Sleep",
}
}
@dataclass
class MiningModeLPM(MinerConfigValue): class MiningModeLPM(MinerConfigValue):
mode: str = field(init=False, default="low") mode: str = field(init=False, default="low")
@@ -128,6 +106,7 @@ 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,14 +126,55 @@ class MiningModeHPM(MinerConfigValue):
return {"mode": {"mode": "turbo"}} return {"mode": {"mode": "turbo"}}
class MiningModePowerTune(MinerConfigValue): @dataclass
class Config: class StandardTuneAlgo(MinerConfigValue):
arbitrary_types_allowed = True mode: str = field(init=False, default="standard")
def as_epic(self) -> str:
return VOptAlgo().as_epic()
@dataclass
class VOptAlgo(MinerConfigValue):
mode: str = field(init=False, default="voltage_optimizer")
def as_epic(self) -> str:
return "VoltageOptimizer"
class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="chip_tune")
def as_epic(self) -> str:
return "ChipTune"
@dataclass
class TunerAlgo(MinerConfigOption):
standard = StandardTuneAlgo
voltage_optimizer = VOptAlgo
chip_tune = ChipTuneAlgo
@classmethod
def default(cls):
return cls.standard()
@classmethod
def from_dict(cls, dict_conf: dict | None):
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
cls_attr = getattr(cls, mode)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)
@dataclass
class MiningModePowerTune(MinerConfigValue):
mode: str = field(init=False, default="power_tuning") mode: str = field(init=False, default="power_tuning")
power: int | None = None power: int = None
algo: TunerAlgoType = field(default_factory=TunerAlgo.default) algo: TunerAlgo = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig | None = None
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune": def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
@@ -163,8 +183,6 @@ class MiningModePowerTune(MinerConfigValue):
cls_conf["power"] = dict_conf["power"] cls_conf["power"] = dict_conf["power"]
if dict_conf.get("algo"): if dict_conf.get("algo"):
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"]) cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
if dict_conf.get("scaling"):
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
return cls(**cls_conf) return cls(**cls_conf)
@@ -179,28 +197,15 @@ class MiningModePowerTune(MinerConfigValue):
return {} return {}
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
tuning_cfg = {"enabled": True, "mode": "power_target"} conf = {"enabled": True, "mode": "power_target"}
if self.power is not None: if self.power is not None:
tuning_cfg["power_target"] = self.power conf["power_target"] = self.power
return {"autotuning": conf}
cfg = {"autotuning": tuning_cfg}
if self.scaling is not None:
scaling_cfg = {"enabled": True}
if self.scaling.step is not None:
scaling_cfg["power_step"] = self.scaling.step
if self.scaling.minimum is not None:
scaling_cfg["min_power_target"] = self.scaling.minimum
if self.scaling.shutdown is not None:
scaling_cfg = {**scaling_cfg, **self.scaling.shutdown.as_bosminer()}
cfg["performance_scaling"] = scaling_cfg
return cfg
def as_boser(self) -> dict: def as_boser(self) -> dict:
cfg = { return {
"set_performance_mode": SetPerformanceModeRequest( "set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_AND_APPLY, save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
mode=PerformanceMode( mode=PerformanceMode(
tuner_mode=TunerPerformanceMode( tuner_mode=TunerPerformanceMode(
power_target=PowerTargetMode( power_target=PowerTargetMode(
@@ -210,50 +215,16 @@ class MiningModePowerTune(MinerConfigValue):
), ),
), ),
} }
if self.scaling is not None:
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
enable=True,
**sd_cfg,
target=DpsTarget(
power_target=DpsPowerTarget(
power_step=Power(self.scaling.step),
min_power_target=Power(self.scaling.minimum),
)
),
),
)
return cfg
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}} return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
def as_mara(self) -> dict:
return {
"mode": {
"work-mode-selector": "Auto",
"concorde": {
"mode-select": "PowerTarget",
"power-target": self.power,
},
}
}
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": True}}
@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
algo: TunerAlgoType = field(default_factory=TunerAlgo.default) algo: TunerAlgo = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig = None
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune": def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
@@ -262,8 +233,6 @@ class MiningModeHashrateTune(MinerConfigValue):
cls_conf["hashrate"] = dict_conf["hashrate"] cls_conf["hashrate"] = dict_conf["hashrate"]
if dict_conf.get("algo"): if dict_conf.get("algo"):
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"]) cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
if dict_conf.get("scaling"):
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
return cls(**cls_conf) return cls(**cls_conf)
@@ -278,11 +247,10 @@ class MiningModeHashrateTune(MinerConfigValue):
conf["hashrate_target"] = self.hashrate conf["hashrate_target"] = self.hashrate
return {"autotuning": conf} return {"autotuning": conf}
@property
def as_boser(self) -> dict: def as_boser(self) -> dict:
cfg = { return {
"set_performance_mode": SetPerformanceModeRequest( "set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_AND_APPLY, save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
mode=PerformanceMode( mode=PerformanceMode(
tuner_mode=TunerPerformanceMode( tuner_mode=TunerPerformanceMode(
hashrate_target=HashrateTargetMode( hashrate_target=HashrateTargetMode(
@@ -294,56 +262,15 @@ class MiningModeHashrateTune(MinerConfigValue):
), ),
) )
} }
if self.scaling is not None:
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
enable=True,
**sd_cfg,
target=DpsTarget(
hashrate_target=DpsHashrateTarget(
hashrate_step=TeraHashrate(self.scaling.step),
min_hashrate_target=TeraHashrate(self.scaling.minimum),
)
),
),
)
return cfg
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}} return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
def as_epic(self) -> dict: def as_epic(self) -> dict:
mode = { return {"ptune": {"algo": self.algo.as_epic(), "target": self.hashrate}}
"ptune": {
"algo": self.algo.as_epic(),
"target": self.hashrate,
}
}
if self.scaling is not None:
if self.scaling.minimum is not None:
mode["ptune"]["min_throttle"] = self.scaling.minimum
if self.scaling.step is not None:
mode["ptune"]["throttle_step"] = self.scaling.step
return mode
def as_mara(self) -> dict:
return {
"mode": {
"work-mode-selector": "Auto",
"concorde": {
"mode-select": "Hashrate",
"hash-target": self.hashrate,
},
}
}
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": True}}
@dataclass
class ManualBoardSettings(MinerConfigValue): class ManualBoardSettings(MinerConfigValue):
freq: float freq: float
volt: float volt: float
@@ -357,10 +284,8 @@ 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")
@@ -381,12 +306,6 @@ class MiningModeManual(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {
"chains": [b.as_vnish() for b in self.boards.values() if b.freq != 0],
"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
@@ -401,35 +320,6 @@ class MiningModeManual(MinerConfigValue):
} }
return cls(global_freq=freq, global_volt=voltage, boards=boards) return cls(global_freq=freq, global_volt=voltage, boards=boards)
@classmethod
def from_epic(cls, epic_conf: dict) -> "MiningModeManual":
voltage = 0
freq = 0
if epic_conf.get("HwConfig") is not None:
freq = epic_conf["HwConfig"]["Boards Target Clock"][0]["Data"]
if epic_conf.get("Power Supply Stats") is not None:
voltage = epic_conf["Power Supply Stats"]["Target Voltage"]
boards = {}
if epic_conf.get("HBs") is not None:
boards = {
board["Index"]: ManualBoardSettings(
freq=board["Core Clock Avg"], volt=board["Input Voltage"]
)
for board in epic_conf["HBs"]
}
return cls(global_freq=freq, global_volt=voltage, boards=boards)
def as_mara(self) -> dict:
return {
"mode": {
"work-mode-selector": "Fixed",
"fixed": {
"frequency": str(self.global_freq),
"voltage": self.global_volt,
},
}
}
class MiningModeConfig(MinerConfigOption): class MiningModeConfig(MinerConfigOption):
normal = MiningModeNormal normal = MiningModeNormal
@@ -478,40 +368,17 @@ class MiningModeConfig(MinerConfigOption):
if tuner_running: if tuner_running:
algo_info = web_conf["PerpetualTune"]["Algorithm"] algo_info = web_conf["PerpetualTune"]["Algorithm"]
if algo_info.get("VoltageOptimizer") is not None: if algo_info.get("VoltageOptimizer") is not None:
scaling_cfg = None
if "Throttle Step" in algo_info["VoltageOptimizer"]:
scaling_cfg = ScalingConfig(
minimum=algo_info["VoltageOptimizer"].get(
"Min Throttle Target"
),
step=algo_info["VoltageOptimizer"].get("Throttle Step"),
)
return cls.hashrate_tuning( return cls.hashrate_tuning(
hashrate=algo_info["VoltageOptimizer"].get("Target"), hashrate=algo_info["VoltageOptimizer"]["Target"],
algo=TunerAlgo.voltage_optimizer(), algo=TunerAlgo.voltage_optimizer,
scaling=scaling_cfg,
)
elif algo_info.get("BoardTune") is not None:
scaling_cfg = None
if "Throttle Step" in algo_info["BoardTune"]:
scaling_cfg = ScalingConfig(
minimum=algo_info["BoardTune"].get("Min Throttle Target"),
step=algo_info["BoardTune"].get("Throttle Step"),
)
return cls.hashrate_tuning(
hashrate=algo_info["BoardTune"].get("Target"),
algo=TunerAlgo.board_tune(),
scaling=scaling_cfg,
) )
else: else:
return cls.hashrate_tuning( return cls.hashrate_tuning(
hashrate=algo_info["ChipTune"].get("Target"), hashrate=algo_info["ChipTune"]["Target"],
algo=TunerAlgo.chip_tune(), algo=TunerAlgo.chip_tune,
) )
else: else:
return MiningModeManual.from_epic(web_conf) return cls.normal()
except KeyError: except KeyError:
return cls.default() return cls.default()
@@ -528,31 +395,18 @@ 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"),
)
if autotuning_conf.get("mode") is not None: if autotuning_conf.get("mode") is not None:
# new autotuning conf # new autotuning conf
mode = autotuning_conf["mode"] mode = autotuning_conf["mode"]
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"], return cls.power_tuning()
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
return cls.power_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
if mode == "hashrate_target": if 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"], return cls.hashrate_tuning()
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
return cls.hashrate_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
@classmethod @classmethod
def from_vnish(cls, web_settings: dict): def from_vnish(cls, web_settings: dict):
@@ -564,7 +418,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(power=int(mode_settings["preset"])) return cls.power_tuning(int(mode_settings["preset"]))
@classmethod @classmethod
def from_boser(cls, grpc_miner_conf: dict): def from_boser(cls, grpc_miner_conf: dict):
@@ -578,40 +432,24 @@ class MiningModeConfig(MinerConfigOption):
if tuner_conf.get("tunerMode") is not None: if tuner_conf.get("tunerMode") is not None:
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"], return cls.power_tuning()
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
)
return cls.power_tuning(
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power")
)
if tuner_conf["tunerMode"] == 2: if tuner_conf["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(
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]), int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
scaling=ScalingConfig.from_boser(
grpc_miner_conf, mode="hashrate"
),
)
return cls.hashrate_tuning(
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
) )
return cls.hashrate_tuning()
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"),
)
if tuner_conf.get("hashrateTarget") is not None: if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning( return cls.hashrate_tuning(
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]), int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
) )
return cls.default()
@classmethod @classmethod
def from_auradine(cls, web_mode: dict): def from_auradine(cls, web_mode: dict):
try: try:
@@ -625,47 +463,8 @@ 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(hashrate=mode_data["Ths"]) return cls.hashrate_tuning(mode_data["Ths"])
if mode_data.get("Power") is not None: if mode_data.get("Power") is not None:
return cls.power_tuning(power=mode_data["Power"]) return cls.power_tuning(mode_data["Power"])
except LookupError: except LookupError:
return cls.default() return cls.default()
@classmethod
def from_mara(cls, web_config: dict):
try:
mode = web_config["mode"]["work-mode-selector"]
if mode == "Fixed":
fixed_conf = web_config["mode"]["fixed"]
return cls.manual(
global_freq=int(fixed_conf["frequency"]),
global_volt=fixed_conf["voltage"],
)
elif mode == "Stock":
return cls.normal()
elif mode == "Sleep":
return cls.sleep()
elif mode == "Auto":
auto_conf = web_config["mode"]["concorde"]
auto_mode = auto_conf["mode-select"]
if auto_mode == "Hashrate":
return cls.hashrate_tuning(hashrate=auto_conf["hash-target"])
elif auto_mode == "PowerTarget":
return cls.power_tuning(power=auto_conf["power-target"])
except LookupError:
pass
return cls.default()
MiningMode = TypeVar(
"MiningMode",
bound=Union[
MiningModeNormal,
MiningModeHPM,
MiningModeLPM,
MiningModeSleep,
MiningModeManual,
MiningModePowerTune,
MiningModeHashrateTune,
],
)

View File

@@ -1,66 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TypeVar, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue
class StandardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
def as_epic(self) -> str:
return VOptAlgo().as_epic()
class VOptAlgo(MinerConfigValue):
mode: str = field(init=False, default="voltage_optimizer")
def as_epic(self) -> str:
return "VoltageOptimizer"
class BoardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="board_tune")
def as_epic(self) -> str:
return "BoardTune"
class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="chip_tune")
def as_epic(self) -> str:
return "ChipTune"
class TunerAlgo(MinerConfigOption):
standard = StandardTuneAlgo
voltage_optimizer = VOptAlgo
board_tune = BoardTuneAlgo
chip_tune = ChipTuneAlgo
@classmethod
def default(cls) -> TunerAlgoType:
return cls.standard()
@classmethod
def from_dict(cls, dict_conf: dict | None) -> TunerAlgoType:
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
cls_attr = getattr(cls, mode)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)
TunerAlgoType = TypeVar(
"TunerAlgoType",
bound=Union[
StandardTuneAlgo,
VOptAlgo,
BoardTuneAlgo,
ChipTuneAlgo,
],
)

View File

@@ -1,131 +0,0 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
class ScalingShutdown(MinerConfigValue):
enabled: bool = False
duration: int = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown":
return cls(
enabled=dict_conf.get("enabled", False), duration=dict_conf.get("duration")
)
@classmethod
def from_bosminer(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdown_enabled")
if sd_enabled is not None:
return cls(
enabled=sd_enabled, duration=power_scaling_conf.get("shutdown_duration")
)
return None
@classmethod
def from_boser(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdownEnabled")
if sd_enabled is not None:
try:
return cls(
enabled=sd_enabled,
duration=power_scaling_conf["shutdownDuration"]["hours"],
)
except KeyError:
return cls(enabled=sd_enabled)
return None
def as_bosminer(self) -> dict:
cfg = {"shutdown_enabled": self.enabled}
if self.duration is not None:
cfg["shutdown_duration"] = self.duration
return cfg
def as_boser(self) -> dict:
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
class ScalingConfig(MinerConfigValue):
step: int = None
minimum: int = None
shutdown: ScalingShutdown = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig":
cls_conf = {
"step": dict_conf.get("step"),
"minimum": dict_conf.get("minimum"),
}
shutdown = dict_conf.get("shutdown")
if shutdown is not None:
cls_conf["shutdown"] = ScalingShutdown.from_dict(shutdown)
return cls(**cls_conf)
@classmethod
def from_bosminer(cls, toml_conf: dict, mode: str = None):
if mode == "power":
return cls._from_bosminer_power(toml_conf)
if mode == "hashrate":
# not implemented yet
pass
@classmethod
def _from_bosminer_power(cls, toml_conf: dict):
power_scaling = toml_conf.get("power_scaling")
if power_scaling is None:
power_scaling = toml_conf.get("performance_scaling")
if power_scaling is not None:
enabled = power_scaling.get("enabled")
if not enabled:
return None
power_step = power_scaling.get("power_step")
min_power = power_scaling.get("min_psu_power_limit")
if min_power is None:
min_power = power_scaling.get("min_power_target")
sd_mode = ScalingShutdown.from_bosminer(power_scaling)
return cls(step=power_step, minimum=min_power, shutdown=sd_mode)
@classmethod
def from_boser(cls, grpc_miner_conf: dict, mode: str = None):
if mode == "power":
return cls._from_boser_power(grpc_miner_conf)
if mode == "hashrate":
# not implemented yet
pass
@classmethod
def _from_boser_power(cls, grpc_miner_conf: dict):
try:
dps_conf = grpc_miner_conf["dps"]
if not dps_conf.get("enabled", False):
return None
except LookupError:
return None
conf = {"shutdown": ScalingShutdown.from_boser(dps_conf)}
if dps_conf.get("minPowerTarget") is not None:
conf["minimum"] = dps_conf["minPowerTarget"]["watt"]
if dps_conf.get("powerStep") is not None:
conf["step"] = dps_conf["powerStep"]["watt"]
return cls(**conf)

View File

@@ -17,20 +17,13 @@ from __future__ import annotations
import random import random
import string import string
from dataclasses import dataclass, field
from typing import List from typing import List
from pydantic import Field
from pyasic.config.base import MinerConfigValue from pyasic.config.base import MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
PoolConfiguration,
PoolGroupConfiguration,
Quota,
SaveAction,
SetPoolGroupsRequest,
)
@dataclass
class Pool(MinerConfigValue): class Pool(MinerConfigValue):
url: str url: str
user: str user: str
@@ -125,36 +118,6 @@ class Pool(MinerConfigValue):
} }
return {"pool": self.url, "login": self.user, "password": self.password} return {"pool": self.url, "login": self.user, "password": self.password}
def as_mara(self, user_suffix: str = None) -> dict:
if user_suffix is not None:
return {
"url": self.url,
"user": f"{self.user}{user_suffix}",
"pass": self.password,
}
return {"url": self.url, "user": self.user, "pass": self.password}
def as_bitaxe(self, user_suffix: str = None) -> dict:
return {
"stratumURL": self.url,
"stratumUser": f"{self.user}{user_suffix}",
"stratumPassword": self.password,
}
def as_boser(self) -> PoolConfiguration:
return PoolConfiguration(
url=self.url, user=self.user, password=self.password, enabled=True
)
def as_vnish(self, user_suffix: str = None) -> dict:
if user_suffix is not None:
return {
"url": self.url,
"user": f"{self.user}{user_suffix}",
"pass": self.password,
}
return {"url": self.url, "user": self.user, "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(
@@ -214,40 +177,12 @@ class Pool(MinerConfigValue):
password=grpc_pool["password"], password=grpc_pool["password"],
) )
@classmethod
def from_mara(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["url"],
user=web_pool["user"],
password=web_pool["pass"],
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "Pool":
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
return cls(
url=url,
user=web_system_info["stratumUser"],
password=web_system_info.get("stratumPassword", ""),
)
@classmethod
def from_luxos(cls, rpc_pools: dict) -> "Pool":
return cls.from_api(rpc_pools)
@classmethod
def from_iceriver(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["addr"],
user=web_pool["user"],
password=web_pool["pass"],
)
@dataclass
class PoolGroup(MinerConfigValue): class PoolGroup(MinerConfigValue):
pools: list[Pool] = Field(default_factory=list) pools: list[Pool] = field(default_factory=list)
quota: int = 1 quota: int = 1
name: str | None = None name: str = None
def __post_init__(self): def __post_init__(self):
if self.name is None: if self.name is None:
@@ -262,7 +197,7 @@ class PoolGroup(MinerConfigValue):
if len(self.pools) > idx: if len(self.pools) > idx:
pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix)) pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix))
else: else:
pools.append(Pool(url="", user="", password="").as_am_modern()) pools.append(Pool("", "", "").as_am_modern())
idx += 1 idx += 1
return pools return pools
@@ -275,7 +210,7 @@ class PoolGroup(MinerConfigValue):
**self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix) **self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix)
) )
else: else:
pools.update(**Pool(url="", user="", password="").as_wm(idx=idx + 1)) pools.update(**Pool("", "", "").as_wm(idx=idx + 1))
idx += 1 idx += 1
return pools return pools
@@ -288,9 +223,7 @@ class PoolGroup(MinerConfigValue):
**self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix) **self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix)
) )
else: else:
pools.update( pools.update(**Pool("", "", "").as_am_old(idx=idx + 1))
**Pool(url="", user="", password="").as_am_old(idx=idx + 1)
)
idx += 1 idx += 1
return pools return pools
@@ -300,7 +233,7 @@ class PoolGroup(MinerConfigValue):
def as_avalon(self, user_suffix: str = None) -> str: def as_avalon(self, user_suffix: str = None) -> str:
if len(self.pools) > 0: if len(self.pools) > 0:
return self.pools[0].as_avalon(user_suffix=user_suffix) return self.pools[0].as_avalon(user_suffix=user_suffix)
return Pool(url="", user="", password="").as_avalon() return Pool("", "", "").as_avalon()
def as_inno(self, user_suffix: str = None) -> dict: def as_inno(self, user_suffix: str = None) -> dict:
pools = {} pools = {}
@@ -311,7 +244,7 @@ class PoolGroup(MinerConfigValue):
**self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix) **self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix)
) )
else: else:
pools.update(**Pool(url="", user="", password="").as_inno(idx=idx + 1)) pools.update(**Pool("", "", "").as_inno(idx=idx + 1))
idx += 1 idx += 1
return pools return pools
@@ -331,25 +264,9 @@ class PoolGroup(MinerConfigValue):
def as_auradine(self, user_suffix: str = None) -> list: def as_auradine(self, user_suffix: str = None) -> list:
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools] return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
def as_epic(self, user_suffix: str = None) -> list: def as_epic(self, user_suffix: str = None) -> dict:
return [p.as_epic(user_suffix=user_suffix) for p in self.pools] return [p.as_epic(user_suffix=user_suffix) for p in self.pools]
def as_mara(self, user_suffix: str = None) -> list:
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
def as_bitaxe(self, user_suffix: str = None) -> dict:
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
def as_boser(self, user_suffix: str = None) -> PoolGroupConfiguration:
return PoolGroupConfiguration(
name=self.name,
quota=Quota(value=self.quota),
pools=[p.as_boser() for p in self.pools],
)
def as_vnish(self, user_suffix: str = None) -> 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 = {}
@@ -384,11 +301,11 @@ class PoolGroup(MinerConfigValue):
@classmethod @classmethod
def from_goldshell(cls, web_pools: list) -> "PoolGroup": def from_goldshell(cls, web_pools: list) -> "PoolGroup":
return cls(pools=[Pool.from_goldshell(p) for p in web_pools]) return cls([Pool.from_goldshell(p) for p in web_pools])
@classmethod @classmethod
def from_inno(cls, web_pools: list) -> "PoolGroup": def from_inno(cls, web_pools: list) -> "PoolGroup":
return cls(pools=[Pool.from_inno(p) for p in web_pools]) return cls([Pool.from_inno(p) for p in web_pools])
@classmethod @classmethod
def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup": def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup":
@@ -402,7 +319,7 @@ 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(pools=[Pool.from_vnish(p) for p in web_settings_pools]) return cls([Pool.from_vnish(p) for p in web_settings_pools])
@classmethod @classmethod
def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup": def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup":
@@ -419,26 +336,10 @@ class PoolGroup(MinerConfigValue):
except LookupError: except LookupError:
return cls() return cls()
@classmethod
def from_mara(cls, web_config_pools: dict) -> "PoolGroup":
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
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":
@@ -498,12 +399,7 @@ class PoolConfig(MinerConfigValue):
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) -> dict:
return { return {}
"set_pool_groups": SetPoolGroupsRequest(
save_action=SaveAction.SAVE_AND_APPLY,
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
)
}
def as_auradine(self, user_suffix: str = None) -> dict: def as_auradine(self, user_suffix: str = None) -> dict:
if len(self.groups) > 0: if len(self.groups) > 0:
@@ -531,20 +427,6 @@ class PoolConfig(MinerConfigValue):
} }
} }
def as_mara(self, user_suffix: str = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
return {"pools": []}
def as_bitaxe(self, user_suffix: str = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
def as_luxos(self, user_suffix: str = None) -> dict:
return {}
def as_vnish(self, user_suffix: str = 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:
@@ -553,38 +435,38 @@ class PoolConfig(MinerConfigValue):
return PoolConfig.default() return PoolConfig.default()
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"])) pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
return cls(groups=[PoolGroup.from_api(pool_data)]) return cls([PoolGroup.from_api(pool_data)])
@classmethod @classmethod
def from_epic(cls, web_conf: dict) -> "PoolConfig": def from_epic(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["StratumConfigs"] pool_data = web_conf["StratumConfigs"]
return cls(groups=[PoolGroup.from_epic(pool_data)]) return cls([PoolGroup.from_epic(pool_data)])
@classmethod @classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig": def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"] pool_data = web_conf["pools"]
return cls(groups=[PoolGroup.from_am_modern(pool_data)]) return cls([PoolGroup.from_am_modern(pool_data)])
@classmethod @classmethod
def from_goldshell(cls, web_pools: list) -> "PoolConfig": def from_goldshell(cls, web_pools: list) -> "PoolConfig":
return cls(groups=[PoolGroup.from_goldshell(web_pools)]) return cls([PoolGroup.from_goldshell(web_pools)])
@classmethod @classmethod
def from_inno(cls, web_pools: list) -> "PoolConfig": def from_inno(cls, web_pools: list) -> "PoolConfig":
return cls(groups=[PoolGroup.from_inno(web_pools)]) return cls([PoolGroup.from_inno(web_pools)])
@classmethod @classmethod
def from_bosminer(cls, toml_conf: dict) -> "PoolConfig": def from_bosminer(cls, toml_conf: dict) -> "PoolConfig":
if toml_conf.get("group") is None: if toml_conf.get("group") is None:
return cls() return cls()
return cls(groups=[PoolGroup.from_bosminer(g) for g in toml_conf["group"]]) return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
@classmethod @classmethod
def from_vnish(cls, web_settings: dict) -> "PoolConfig": def from_vnish(cls, web_settings: dict) -> "PoolConfig":
try: try:
return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])]) return cls([PoolGroup.from_vnish(web_settings["miner"]["pools"])])
except LookupError: except LookupError:
return cls() return cls()
@@ -599,32 +481,3 @@ class PoolConfig(MinerConfigValue):
) )
except LookupError: except LookupError:
return cls() return cls()
@classmethod
def from_mara(cls, web_config: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
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"]
]
)

View File

@@ -0,0 +1,221 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass, field
from pyasic.config.base import MinerConfigOption, MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
DpsPowerTarget,
DpsTarget,
Power,
SetDpsRequest,
)
@dataclass
class PowerScalingShutdownEnabled(MinerConfigValue):
mode: str = field(init=False, default="enabled")
duration: int = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingShutdownEnabled":
return cls(duration=dict_conf.get("duration"))
def as_bosminer(self) -> dict:
cfg = {"shutdown_enabled": True}
if self.duration is not None:
cfg["shutdown_duration"] = self.duration
return cfg
def as_boser(self) -> dict:
return {"enable_shutdown": True, "shutdown_duration": self.duration}
@dataclass
class PowerScalingShutdownDisabled(MinerConfigValue):
mode: str = field(init=False, default="disabled")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingShutdownDisabled":
return cls()
def as_bosminer(self) -> dict:
return {"shutdown_enabled": False}
def as_boser(self) -> dict:
return {"enable_shutdown ": False}
class PowerScalingShutdown(MinerConfigOption):
enabled = PowerScalingShutdownEnabled
disabled = PowerScalingShutdownDisabled
@classmethod
def from_dict(cls, dict_conf: dict | None):
if dict_conf is None:
return cls.default()
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
clsattr = getattr(cls, mode)
if clsattr is not None:
return clsattr().from_dict(dict_conf)
@classmethod
def from_bosminer(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdown_enabled")
if sd_enabled is not None:
if sd_enabled:
return cls.enabled(power_scaling_conf.get("shutdown_duration"))
else:
return cls.disabled()
return None
@classmethod
def from_boser(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdownEnabled")
if sd_enabled is not None:
if sd_enabled:
try:
return cls.enabled(power_scaling_conf["shutdownDuration"]["hours"])
except KeyError:
return cls.enabled()
else:
return cls.disabled()
return None
@dataclass
class PowerScalingEnabled(MinerConfigValue):
mode: str = field(init=False, default="enabled")
power_step: int = None
minimum_power: int = None
shutdown_enabled: PowerScalingShutdownEnabled | PowerScalingShutdownDisabled = None
@classmethod
def from_bosminer(cls, power_scaling_conf: dict) -> "PowerScalingEnabled":
power_step = power_scaling_conf.get("power_step")
min_power = power_scaling_conf.get("min_psu_power_limit")
if min_power is None:
min_power = power_scaling_conf.get("min_power_target")
sd_mode = PowerScalingShutdown.from_bosminer(power_scaling_conf)
return cls(
power_step=power_step, minimum_power=min_power, shutdown_enabled=sd_mode
)
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingEnabled":
cls_conf = {
"power_step": dict_conf.get("power_step"),
"minimum_power": dict_conf.get("minimum_power"),
}
shutdown_enabled = dict_conf.get("shutdown_enabled")
if shutdown_enabled is not None:
cls_conf["shutdown_enabled"] = PowerScalingShutdown.from_dict(
shutdown_enabled
)
return cls(**cls_conf)
def as_bosminer(self) -> dict:
cfg = {"enabled": True}
if self.power_step is not None:
cfg["power_step"] = self.power_step
if self.minimum_power is not None:
cfg["min_power_target"] = self.minimum_power
if self.shutdown_enabled is not None:
cfg = {**cfg, **self.shutdown_enabled.as_bosminer()}
return {"performance_scaling": cfg}
def as_boser(self) -> dict:
return {
"set_dps": SetDpsRequest(
enable=True,
**self.shutdown_enabled.as_boser(),
target=DpsTarget(
power_target=DpsPowerTarget(
power_step=Power(self.power_step),
min_power_target=Power(self.minimum_power),
)
),
),
}
@dataclass
class PowerScalingDisabled(MinerConfigValue):
mode: str = field(init=False, default="disabled")
class PowerScalingConfig(MinerConfigOption):
enabled = PowerScalingEnabled
disabled = PowerScalingDisabled
@classmethod
def default(cls):
return cls.disabled()
@classmethod
def from_dict(cls, dict_conf: dict | None):
if dict_conf is None:
return cls.default()
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
clsattr = getattr(cls, mode)
if clsattr is not None:
return clsattr().from_dict(dict_conf)
@classmethod
def from_bosminer(cls, toml_conf: dict):
power_scaling = toml_conf.get("power_scaling")
if power_scaling is None:
power_scaling = toml_conf.get("performance_scaling")
if power_scaling is not None:
enabled = power_scaling.get("enabled")
if enabled is not None:
if enabled:
return cls.enabled().from_bosminer(power_scaling)
else:
return cls.disabled()
return cls.default()
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
dps_conf = grpc_miner_conf["dps"]
if not dps_conf.get("enabled", False):
return cls.disabled()
except LookupError:
return cls.default()
conf = {"shutdown_enabled": PowerScalingShutdown.from_boser(dps_conf)}
if dps_conf.get("minPowerTarget") is not None:
conf["minimum_power"] = dps_conf["minPowerTarget"]["watt"]
if dps_conf.get("powerStep") is not None:
conf["power_step"] = dps_conf["powerStep"]["watt"]
return cls.enabled(**conf)

View File

@@ -20,10 +20,11 @@ from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue from pyasic.config.base import MinerConfigValue
@dataclass
class TemperatureConfig(MinerConfigValue): class TemperatureConfig(MinerConfigValue):
target: int | None = None target: int = None
hot: int | None = None hot: int = None
danger: int | None = None danger: int = None
@classmethod @classmethod
def default(cls): def default(cls):
@@ -37,8 +38,6 @@ class TemperatureConfig(MinerConfigValue):
temp_cfg["hot_temp"] = self.hot temp_cfg["hot_temp"] = self.hot
if self.danger is not None: if self.danger is not None:
temp_cfg["dangerous_temp"] = self.danger temp_cfg["dangerous_temp"] = self.danger
if len(temp_cfg) == 0:
return {}
return {"temp_control": temp_cfg} return {"temp_control": temp_cfg}
def as_epic(self) -> dict: def as_epic(self) -> dict:
@@ -48,17 +47,9 @@ class TemperatureConfig(MinerConfigValue):
else: else:
temps_config["fans"]["Auto"]["Target Temperature"] = 60 temps_config["fans"]["Auto"]["Target Temperature"] = 60
if self.danger is not None: if self.danger is not None:
temps_config["temps"]["critical"] = self.danger temps_config["temps"]["shutdown"] = self.danger
if self.hot is not None:
temps_config["temps"]["shutdown"] = self.hot
return temps_config return temps_config
def as_luxos(self) -> dict:
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
def as_vnish(self) -> dict:
return {"misc": {"restart_temp": self.danger}}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig": def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
return cls( return cls(
@@ -76,38 +67,26 @@ class TemperatureConfig(MinerConfigValue):
hot=temp_control.get("hot_temp"), hot=temp_control.get("hot_temp"),
danger=temp_control.get("dangerous_temp"), danger=temp_control.get("dangerous_temp"),
) )
return cls()
@classmethod @classmethod
def from_epic(cls, web_conf: dict) -> "TemperatureConfig": def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
try: try:
dangerous_temp = web_conf["Misc"]["Critical Temp"] dangerous_temp = web_conf["Misc"]["Shutdown Temp"]
except KeyError: except KeyError:
dangerous_temp = None dangerous_temp = None
try:
hot_temp = web_conf["Misc"]["Shutdown Temp"]
except KeyError:
hot_temp = None
# Need to do this in two blocks to avoid KeyError if one is missing # Need to do this in two blocks to avoid KeyError if one is missing
try: try:
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"] target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
except KeyError: except KeyError:
target_temp = None target_temp = None
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp) return cls(target=target_temp, danger=dangerous_temp)
@classmethod @classmethod
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig": def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":
try:
dangerous_temp = web_settings["misc"]["restart_temp"]
except KeyError:
dangerous_temp = None
try: try:
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto": if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
return cls( return cls(target=web_settings["miner"]["cooling"]["mode"]["param"])
target=web_settings["miner"]["cooling"]["mode"]["param"],
danger=dangerous_temp,
)
except KeyError: except KeyError:
pass pass
return cls() return cls()
@@ -142,16 +121,3 @@ class TemperatureConfig(MinerConfigValue):
return cls(**conf) return cls(**conf)
return cls.default() return cls.default()
@classmethod
def from_luxos(cls, rpc_tempctrl: dict) -> "TemperatureConfig":
try:
tempctrl_config = rpc_tempctrl["TEMPCTRL"][0]
return cls(
target=tempctrl_config.get("Target"),
hot=tempctrl_config.get("Hot"),
danger=tempctrl_config.get("Dangerous"),
)
except LookupError:
pass
return cls.default()

View File

@@ -15,24 +15,22 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
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, field_serializer
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune from pyasic.config.mining import MiningModePowerTune
from pyasic.data.pools import PoolMetrics, Scheme
from pyasic.device.algorithm.hashrate import AlgoHashRateType
from .boards import HashBoard from .boards import HashBoard
from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .fans import Fan from .fans import Fan
class MinerData(BaseModel): @dataclass
class MinerData:
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`) """A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
Attributes: Attributes:
@@ -40,24 +38,20 @@ class MinerData(BaseModel):
datetime: The time and date this data was generated. datetime: The time and date this data was generated.
uptime: The uptime of the miner in seconds. uptime: The uptime of the miner in seconds.
mac: The MAC address of the miner as a str. mac: The MAC address of the miner as a str.
device_info: Info about the device, such as model, make, and firmware.
model: The model of the miner as a str. model: The model of the miner as a str.
make: The make of the miner as a str. make: The make of the miner as a str.
firmware: The firmware on the miner as a str.
algo: The mining algorithm of the miner as a str.
api_ver: The current api version on the miner as a str. api_ver: The current api version on the miner as a str.
fw_ver: The current firmware version on the miner as a str. fw_ver: The current firmware version on the miner as a str.
hostname: The network hostname of the miner as a str. hostname: The network hostname of the miner as a str.
hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically. hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically.
_hashrate: Backup for hashrate found via API instead of hashboards.
expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float. expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics. hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics.
temperature_avg: The average temperature across the boards. Calculated automatically. temperature_avg: The average temperature across the boards. Calculated automatically.
env_temp: The environment temps as a float. env_temp: The environment temps as a float.
wattage: Current power draw of the miner as an int. wattage: Current power draw of the miner as an int.
voltage: Current output voltage of the PSU as an float.
wattage_limit: Power limit of the miner as an int. wattage_limit: Power limit of the miner as an int.
fans: A list of fans on the miner with their speeds. fans: A list of fans on the miner with their speeds.
expected_fans: The number of fans expected on a miner.
fan_psu: The speed of the PSU on the fan if the miner collects it. fan_psu: The speed of the PSU on the fan if the miner collects it.
total_chips: The total number of chips on all boards. Calculated automatically. total_chips: The total number of chips on all boards. Calculated automatically.
expected_chips: The expected number of chips in the miner as an int. expected_chips: The expected number of chips in the miner as an int.
@@ -70,51 +64,36 @@ class MinerData(BaseModel):
fault_light: Whether the fault light is on as a boolean. fault_light: Whether the fault light is on as a boolean.
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically. efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
is_mining: Whether the miner is mining. is_mining: Whether the miner is mining.
pools: A list of PoolMetrics instances, each representing metrics for a pool.
""" """
# general
ip: str ip: str
raw_datetime: datetime = Field( datetime: datetime = None
exclude=True, default_factory=datetime.now(timezone.utc).astimezone, repr=False uptime: int = None
) mac: str = None
model: str = None
# about make: str = None
device_info: DeviceInfo | None = None api_ver: str = None
mac: str | None = None fw_ver: str = None
api_ver: str | None = None hostname: str = None
fw_ver: str | None = None hashrate: float = field(init=False)
hostname: str | None = None _hashrate: float = field(repr=False, default=None)
expected_hashrate: float = None
# hashrate hashboards: List[HashBoard] = field(default_factory=list)
raw_hashrate: AlgoHashRateType = Field(exclude=True, default=None, repr=False) expected_hashboards: int = None
temperature_avg: int = field(init=False)
# expected env_temp: float = None
expected_hashrate: AlgoHashRateType | None = None wattage: int = None
expected_hashboards: int | None = None wattage_limit: int = field(init=False)
expected_chips: int | None = None _wattage_limit: int = field(repr=False, default=None)
expected_fans: int | None = None fans: List[Fan] = field(default_factory=list)
fan_psu: int = None
# temperature total_chips: int = field(init=False)
env_temp: int | None = None expected_chips: int = None
percent_expected_chips: float = field(init=False)
# power percent_expected_hashrate: float = field(init=False)
wattage: int | None = None percent_expected_wattage: float = field(init=False)
voltage: float | None = None nominal: bool = field(init=False)
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False) config: MinerConfig = None
# fans
fans: List[Fan] = Field(default_factory=list)
fan_psu: int | None = None
# boards
hashboards: List[HashBoard] = Field(default_factory=list)
# config
config: MinerConfig | None = None
fault_light: bool | None = None
# errors
errors: List[ errors: List[
Union[ Union[
WhatsminerError, WhatsminerError,
@@ -122,21 +101,21 @@ class MinerData(BaseModel):
X19Error, X19Error,
InnosiliconError, InnosiliconError,
] ]
] = Field(default_factory=list) ] = field(default_factory=list)
fault_light: Union[bool, None] = None
# mining state efficiency: int = field(init=False)
is_mining: bool = True is_mining: bool = True
uptime: int | None = None
# pools
pools: list[PoolMetrics] = Field(default_factory=list)
@classmethod @classmethod
def fields(cls): def fields(cls):
return list(cls.model_fields.keys()) return [f.name for f in fields(cls) if not f.name.startswith("_")]
@staticmethod
def dict_factory(x):
return {k: v for (k, v) in x if not k.startswith("_")}
def __post_init__(self): def __post_init__(self):
self.raw_datetime = datetime.now(timezone.utc).astimezone() 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:
@@ -164,19 +143,19 @@ class MinerData(BaseModel):
def __floordiv__(self, other): def __floordiv__(self, other):
cp = copy.deepcopy(self) cp = copy.deepcopy(self)
for key in self.fields(): for key in self:
item = getattr(self, key) item = getattr(self, key)
if isinstance(item, int): if isinstance(item, int):
setattr(cp, key, item // other) setattr(cp, key, item // other)
if isinstance(item, float): if isinstance(item, float):
setattr(cp, key, item / other) setattr(cp, key, round(item / other, 2))
return cp return cp
def __add__(self, other): def __add__(self, other):
if not isinstance(other, MinerData): if not isinstance(other, MinerData):
raise TypeError("Cannot add MinerData to non MinerData type.") raise TypeError("Cannot add MinerData to non MinerData type.")
cp = copy.deepcopy(self) cp = copy.deepcopy(self)
for key in self.fields(): for key in self:
item = getattr(self, key) item = getattr(self, key)
other_item = getattr(other, key) other_item = getattr(other, key)
if item is None: if item is None:
@@ -196,49 +175,34 @@ class MinerData(BaseModel):
setattr(cp, key, item & other_item) setattr(cp, key, item & other_item)
return cp return cp
@computed_field # type: ignore[misc]
@property @property
def hashrate(self) -> AlgoHashRateType: def hashrate(self): # noqa - Skip PyCharm inspection
if len(self.hashboards) > 0: if len(self.hashboards) > 0:
hr_data = [] hr_data = []
for item in self.hashboards: for item in self.hashboards:
if item.hashrate is not None: if item.hashrate is not None:
hr_data.append(item.hashrate) hr_data.append(item.hashrate)
if len(hr_data) > 0: if len(hr_data) > 0:
return sum(hr_data, start=type(hr_data[0])(rate=0)) return round(sum(hr_data), 2)
return self.raw_hashrate return self._hashrate
@field_serializer("hashrate")
def serialize_hashrate(self, hashrate: AlgoHashRateType | None) -> float:
if hashrate is not None:
return float(hashrate)
@field_serializer("expected_hashrate")
def serialize_expected_hashrate(
self, expected_hashrate: AlgoHashRateType | None, _info
) -> float:
if expected_hashrate is not None:
return float(expected_hashrate)
@hashrate.setter @hashrate.setter
def hashrate(self, val): def hashrate(self, val):
self.raw_hashrate = val self._hashrate = val
@computed_field # type: ignore[misc]
@property @property
def wattage_limit(self) -> int: def wattage_limit(self): # noqa - Skip PyCharm inspection
if self.config is not None: if self.config is not None:
if isinstance(self.config.mining_mode, MiningModePowerTune): if isinstance(self.config.mining_mode, MiningModePowerTune):
return self.config.mining_mode.power return self.config.mining_mode.power
return self.raw_wattage_limit return self._wattage_limit
@wattage_limit.setter @wattage_limit.setter
def wattage_limit(self, val: int): def wattage_limit(self, val: int):
self.raw_wattage_limit = val self._wattage_limit = val
@computed_field # type: ignore[misc]
@property @property
def total_chips(self) -> int | None: def total_chips(self): # noqa - Skip PyCharm inspection
if len(self.hashboards) > 0: if len(self.hashboards) > 0:
chip_data = [] chip_data = []
for item in self.hashboards: for item in self.hashboards:
@@ -248,45 +212,58 @@ class MinerData(BaseModel):
return sum(chip_data) return sum(chip_data)
return None return None
@computed_field # type: ignore[misc] @total_chips.setter
def total_chips(self, val):
pass
@property @property
def nominal(self) -> bool | None: def nominal(self): # noqa - Skip PyCharm inspection
if self.total_chips is None or self.expected_chips is None: if self.total_chips is None or self.expected_chips is None:
return None return None
return self.expected_chips == self.total_chips return self.expected_chips == self.total_chips
@computed_field # type: ignore[misc] @nominal.setter
def nominal(self, val):
pass
@property @property
def percent_expected_chips(self) -> int | None: def percent_expected_chips(self): # noqa - Skip PyCharm inspection
if self.total_chips is None or self.expected_chips is None: if self.total_chips is None or self.expected_chips is None:
return None return None
if self.total_chips == 0 or self.expected_chips == 0: if self.total_chips == 0 or self.expected_chips == 0:
return 0 return 0
return round((self.total_chips / self.expected_chips) * 100) return round((self.total_chips / self.expected_chips) * 100)
@computed_field # type: ignore[misc] @percent_expected_chips.setter
def percent_expected_chips(self, val):
pass
@property @property
def percent_expected_hashrate(self) -> int | None: def percent_expected_hashrate(self): # noqa - Skip PyCharm inspection
if self.hashrate is None or self.expected_hashrate is None: if self.hashrate is None or self.expected_hashrate is None:
return None return None
try: if self.hashrate == 0 or self.expected_hashrate == 0:
return round((self.hashrate / self.expected_hashrate) * 100)
except ZeroDivisionError:
return 0 return 0
return round((self.hashrate / self.expected_hashrate) * 100)
@percent_expected_hashrate.setter
def percent_expected_hashrate(self, val):
pass
@computed_field # type: ignore[misc]
@property @property
def percent_expected_wattage(self) -> int | None: def percent_expected_wattage(self): # noqa - Skip PyCharm inspection
if self.wattage_limit is None or self.wattage is None: if self.wattage_limit is None or self.wattage is None:
return None return None
try: if self.wattage_limit == 0 or self.wattage == 0:
return round((self.wattage / self.wattage_limit) * 100)
except ZeroDivisionError:
return 0 return 0
return round((self.wattage / self.wattage_limit) * 100)
@percent_expected_wattage.setter
def percent_expected_wattage(self, val):
pass
@computed_field # type: ignore[misc]
@property @property
def temperature_avg(self) -> int | None: def temperature_avg(self): # noqa - Skip PyCharm inspection
total_temp = 0 total_temp = 0
temp_count = 0 temp_count = 0
for hb in self.hashboards: for hb in self.hashboards:
@@ -297,55 +274,24 @@ class MinerData(BaseModel):
return None return None
return round(total_temp / temp_count) return round(total_temp / temp_count)
@computed_field # type: ignore[misc] @temperature_avg.setter
def temperature_avg(self, val):
pass
@property @property
def efficiency(self) -> int | None: def efficiency(self): # noqa - Skip PyCharm inspection
if self.hashrate is None or self.wattage is None: if self.hashrate is None or self.wattage is None:
return None return None
try: if self.hashrate == 0 or self.wattage == 0:
return round(self.wattage / float(self.hashrate))
except ZeroDivisionError:
return 0 return 0
return round(self.wattage / self.hashrate)
@computed_field # type: ignore[misc] @efficiency.setter
@property def efficiency(self, val):
def datetime(self) -> str: pass
return self.raw_datetime.isoformat()
@computed_field # type: ignore[misc]
@property
def timestamp(self) -> int:
return int(time.mktime(self.raw_datetime.timetuple()))
@computed_field # type: ignore[misc]
@property
def make(self) -> str:
if self.device_info.make is not None:
return str(self.device_info.make)
@computed_field # type: ignore[misc]
@property
def model(self) -> str:
if self.device_info.model is not None:
return str(self.device_info.model)
@computed_field # type: ignore[misc]
@property
def firmware(self) -> str:
if self.device_info.firmware is not None:
return str(self.device_info.firmware)
@computed_field # type: ignore[misc]
@property
def algo(self) -> str:
if self.device_info.algo is not None:
return str(self.device_info.algo)
def keys(self) -> list:
return list(self.model_fields.keys())
def asdict(self) -> dict: def asdict(self) -> dict:
return self.model_dump() return asdict(self, dict_factory=self.dict_factory)
def as_dict(self) -> dict: def as_dict(self) -> dict:
"""Get this dataclass as a dictionary. """Get this dataclass as a dictionary.
@@ -361,7 +307,9 @@ class MinerData(BaseModel):
Returns: Returns:
A JSON version of this class. A JSON version of this class.
""" """
return self.model_dump_json() data = self.asdict()
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
return json.dumps(data)
def as_csv(self) -> str: def as_csv(self) -> str:
"""Get this dataclass as CSV. """Get this dataclass as CSV.
@@ -370,6 +318,7 @@ class MinerData(BaseModel):
A CSV version of this class with no headers. A CSV version of this class with no headers.
""" """
data = self.asdict() data = self.asdict()
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
errs = [] errs = []
for error in data["errors"]: for error in data["errors"]:
errs.append(error["error_message"]) errs.append(error["error_message"])
@@ -390,7 +339,7 @@ class MinerData(BaseModel):
field_data = [] field_data = []
tags = ["ip", "mac", "model", "hostname"] tags = ["ip", "mac", "model", "hostname"]
for attribute in self.fields(): for attribute in self:
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}")
@@ -434,6 +383,6 @@ class MinerData(BaseModel):
tags_str = ",".join(tag_data) tags_str = ",".join(tag_data)
field_str = ",".join(field_data) field_str = ",".join(field_data)
timestamp = str(self.timestamp * 1e9) timestamp = str(int(time.mktime(self.datetime.timetuple()) * 1e9))
return " ".join([tags_str, field_str, timestamp]) return " ".join([tags_str, field_str, timestamp])

View File

@@ -13,16 +13,13 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from typing import Any from typing import Any
from pydantic import BaseModel, field_serializer
from pyasic.device.algorithm.hashrate import AlgoHashRateType @dataclass
class HashBoard:
class HashBoard(BaseModel):
"""A Dataclass to standardize hashboard data. """A Dataclass to standardize hashboard data.
Attributes: Attributes:
@@ -34,27 +31,16 @@ class HashBoard(BaseModel):
expected_chips: The expected chip count of the board as an int. expected_chips: The expected chip count of the board as an int.
serial_number: The serial number of the board. serial_number: The serial number of the board.
missing: Whether the board is returned from the miners data as a bool. missing: Whether the board is returned from the miners data as a bool.
tuned: Whether the board is tuned as a bool.
active: Whether the board is currently tuning as a bool.
voltage: Current input voltage of the board as a float.
""" """
slot: int = 0 slot: int = 0
hashrate: AlgoHashRateType | None = None hashrate: float = None
temp: float | None = None temp: int = None
chip_temp: float | None = None chip_temp: int = None
chips: int | None = None chips: int = None
expected_chips: int | None = None expected_chips: int = None
serial_number: str | None = None serial_number: str = None
missing: bool = True missing: bool = True
tuned: bool | None = None
active: bool | None = None
voltage: float | None = None
@field_serializer("hashrate")
def serialize_hashrate(self, hashrate: AlgoHashRateType | None) -> float:
if hashrate is not None:
return float(hashrate)
def get(self, __key: str, default: Any = None): def get(self, __key: str, default: Any = None):
try: try:

View File

@@ -1,31 +0,0 @@
from pydantic import BaseModel, ConfigDict, field_serializer
from pyasic.device.algorithm import MinerAlgoType
from pyasic.device.firmware import MinerFirmware
from pyasic.device.makes import MinerMake
from pyasic.device.models import MinerModelType
class DeviceInfo(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
make: MinerMake | None = None
model: MinerModelType | None = None
firmware: MinerFirmware | None = None
algo: type[MinerAlgoType] | None = None
@field_serializer("make")
def serialize_make(self, make: MinerMake, _info):
return str(make)
@field_serializer("model")
def serialize_model(self, model: MinerModelType, _info):
return str(model)
@field_serializer("firmware")
def serialize_firmware(self, firmware: MinerFirmware, _info):
return str(firmware)
@field_serializer("algo")
def serialize_algo(self, algo: MinerAlgoType, _info):
return str(algo)

View File

@@ -13,10 +13,12 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.data.error_codes.base import BaseMinerError
from dataclasses import asdict, dataclass, fields
class X19Error(BaseMinerError): @dataclass
class X19Error:
"""A Dataclass to handle error codes of X19 miners. """A Dataclass to handle error codes of X19 miners.
Attributes: Attributes:
@@ -26,3 +28,10 @@ class X19Error(BaseMinerError):
error_message: str error_message: str
error_code: int = 0 error_code: int = 0
@classmethod
def fields(cls):
return fields(cls)
def asdict(self):
return asdict(self)

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel
class BaseMinerError(BaseModel):
@classmethod
def fields(cls):
return list(cls.model_fields.keys())
def asdict(self) -> dict:
return self.model_dump()
def as_dict(self) -> dict:
"""Get this dataclass as a dictionary.
Returns:
A dictionary version of this class.
"""
return self.asdict()

View File

@@ -14,10 +14,11 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.data.error_codes.base import BaseMinerError from dataclasses import asdict, dataclass, fields
class BraiinsOSError(BaseMinerError): @dataclass
class BraiinsOSError:
"""A Dataclass to handle error codes of BraiinsOS+ miners. """A Dataclass to handle error codes of BraiinsOS+ miners.
Attributes: Attributes:
@@ -27,3 +28,10 @@ class BraiinsOSError(BaseMinerError):
error_message: str error_message: str
error_code: int = 0 error_code: int = 0
@classmethod
def fields(cls):
return fields(cls)
def asdict(self):
return asdict(self)

View File

@@ -14,13 +14,11 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from dataclasses import asdict, dataclass, field, fields
from pydantic import computed_field
from pyasic.data.error_codes.base import BaseMinerError
class InnosiliconError(BaseMinerError): @dataclass
class InnosiliconError:
"""A Dataclass to handle error codes of Innosilicon miners. """A Dataclass to handle error codes of Innosilicon miners.
Attributes: Attributes:
@@ -29,14 +27,25 @@ class InnosiliconError(BaseMinerError):
""" """
error_code: int error_code: int
error_message: str = field(init=False)
@classmethod
def fields(cls):
return fields(cls)
@computed_field # type: ignore[misc]
@property @property
def error_message(self) -> str: # noqa - Skip PyCharm inspection def error_message(self): # noqa - Skip PyCharm inspection
if self.error_code in ERROR_CODES: if self.error_code in ERROR_CODES:
return ERROR_CODES[self.error_code] return ERROR_CODES[self.error_code]
return "Unknown error type." return "Unknown error type."
@error_message.setter
def error_message(self, val):
pass
def asdict(self):
return asdict(self)
ERROR_CODES = { ERROR_CODES = {
21: "The PLUG signal of the hash board is not detected.", 21: "The PLUG signal of the hash board is not detected.",

View File

@@ -13,12 +13,12 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pydantic import computed_field
from pyasic.data.error_codes.base import BaseMinerError from dataclasses import asdict, dataclass, field, fields
class WhatsminerError(BaseMinerError): @dataclass
class WhatsminerError:
"""A Dataclass to handle error codes of Whatsminers. """A Dataclass to handle error codes of Whatsminers.
Attributes: Attributes:
@@ -27,10 +27,14 @@ class WhatsminerError(BaseMinerError):
""" """
error_code: int error_code: int
error_message: str = field(init=False)
@classmethod
def fields(cls):
return fields(cls)
@computed_field # type: ignore[misc]
@property @property
def error_message(self) -> str: # noqa - Skip PyCharm inspection def error_message(self): # noqa - Skip PyCharm inspection
if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1": if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1":
err_type = int(str(self.error_code)[:2]) err_type = int(str(self.error_code)[:2])
err_subtype = int(str(self.error_code)[2:3]) err_subtype = int(str(self.error_code)[2:3])
@@ -70,6 +74,13 @@ class WhatsminerError(BaseMinerError):
except KeyError: except KeyError:
return "Unknown error type." return "Unknown error type."
@error_message.setter
def error_message(self, val):
pass
def asdict(self):
return asdict(self)
ERROR_CODES = { ERROR_CODES = {
1: { # Fan error 1: { # Fan error

View File

@@ -14,12 +14,12 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from dataclasses import dataclass
from typing import Any from typing import Any
from pydantic import BaseModel
@dataclass
class Fan(BaseModel): class Fan:
"""A Dataclass to standardize fan data. """A Dataclass to standardize fan data.
Attributes: Attributes:

View File

@@ -1,94 +0,0 @@
from enum import Enum
from typing import Optional
from urllib.parse import urlparse
from pydantic import BaseModel, computed_field, model_serializer
from typing_extensions import Self
class Scheme(Enum):
STRATUM_V1 = "stratum+tcp"
STRATUM_V2 = "stratum2+tcp"
STRATUM_V1_SSL = "stratum+ssl"
class PoolUrl(BaseModel):
scheme: Scheme
host: str
port: int
pubkey: Optional[str] = None
@model_serializer
def serialize(self):
return str(self)
def __str__(self) -> str:
if self.scheme == Scheme.STRATUM_V2 and self.pubkey:
return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}"
else:
return f"{self.scheme.value}://{self.host}:{self.port}"
@classmethod
def from_str(cls, url: str) -> Self | None:
parsed_url = urlparse(url)
if not parsed_url.hostname:
return None
if not parsed_url.scheme.strip() == "":
scheme = Scheme(parsed_url.scheme)
else:
scheme = Scheme.STRATUM_V1
host = parsed_url.hostname
port = parsed_url.port
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
class PoolMetrics(BaseModel):
"""A dataclass to standardize pool metrics returned from miners.
Attributes:
accepted: Number of accepted shares.
rejected: Number of rejected shares.
get_failures: Number of failures in obtaining work from the pool.
remote_failures: Number of failures communicating with the pool server.
active: Indicates if the miner is connected to the stratum server.
Alive : Indicates if a pool is alive.
url: URL of the pool.
index: Index of the pool.
user: Username for the pool.
pool_rejected_percent: Percentage of rejected shares by the pool.
pool_stale_percent: Percentage of stale shares by the pool.
"""
url: PoolUrl | None
accepted: int | None = None
rejected: int | None = None
get_failures: int | None = None
remote_failures: int | None = None
active: bool | None = None
alive: bool | None = None
index: int | None = None
user: str | None = None
@computed_field # type: ignore[misc]
@property
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of rejected shares"""
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
@computed_field # type: ignore[misc]
@property
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of stale shares."""
return self._calculate_percentage(
self.get_failures, self.accepted + self.rejected
)
@staticmethod
def _calculate_percentage(value: int, total: int) -> float:
"""Calculate the percentage."""
if value is None or total is None:
return 0
if total == 0:
return 0
return (value / total) * 100

View File

@@ -1,4 +0,0 @@
from .algorithm import MinerAlgo
from .firmware import MinerFirmware
from .makes import MinerMake
from .models import MinerModel

View File

@@ -1,26 +0,0 @@
from .base import MinerAlgoType
from .blake256 import Blake256Algo
from .eaglesong import EaglesongAlgo
from .equihash import EquihashAlgo
from .ethash import EtHashAlgo
from .handshake import HandshakeAlgo
from .hashrate import *
from .hashrate.unit import *
from .kadena import KadenaAlgo
from .kheavyhash import KHeavyHashAlgo
from .scrypt import ScryptAlgo
from .sha256 import SHA256Algo
from .x11 import X11Algo
class MinerAlgo:
SHA256 = SHA256Algo
SCRYPT = ScryptAlgo
KHEAVYHASH = KHeavyHashAlgo
KADENA = KadenaAlgo
HANDSHAKE = HandshakeAlgo
X11 = X11Algo
BLAKE256 = Blake256Algo
EAGLESONG = EaglesongAlgo
ETHASH = EtHashAlgo
EQUIHASH = EquihashAlgo

View File

@@ -1,16 +0,0 @@
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]

View File

@@ -1,13 +0,0 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import Blake256HashRate
from .hashrate.unit import Blake256Unit
# make this json serializable
class Blake256Algo(MinerAlgoType):
hashrate: type[Blake256HashRate] = Blake256HashRate
unit: type[Blake256Unit] = Blake256Unit
name = "Blake256"

View File

@@ -1,13 +0,0 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import EaglesongHashRate
from .hashrate.unit import EaglesongUnit
# make this json serializable
class EaglesongAlgo(MinerAlgoType):
hashrate: type[EaglesongHashRate] = EaglesongHashRate
unit: type[EaglesongUnit] = EaglesongUnit
name = "Eaglesong"

View File

@@ -1,13 +0,0 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import EquihashHashRate
from .hashrate.unit import EquihashUnit
# make this json serializable
class EquihashAlgo(MinerAlgoType):
hashrate: type[EquihashHashRate] = EquihashHashRate
unit: type[EquihashUnit] = EquihashUnit
name = "Equihash"

View File

@@ -1,12 +0,0 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import EtHashHashRate
from .hashrate.unit import EtHashUnit
class EtHashAlgo(MinerAlgoType):
hashrate: type[EtHashHashRate] = EtHashHashRate
unit: type[EtHashUnit] = EtHashUnit
name = "EtHash"

View File

@@ -1,13 +0,0 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import HandshakeHashRate
from .hashrate.unit import HandshakeUnit
# make this json serializable
class HandshakeAlgo(MinerAlgoType):
hashrate: type[HandshakeHashRate] = HandshakeHashRate
unit: type[HandshakeUnit] = HandshakeUnit
name = "Handshake"

View File

@@ -1,24 +0,0 @@
from .base import AlgoHashRateType
from .blake256 import Blake256HashRate
from .eaglesong import EaglesongHashRate
from .equihash import EquihashHashRate
from .ethash import EtHashHashRate
from .handshake import HandshakeHashRate
from .kadena import KadenaHashRate
from .kheavyhash import KHeavyHashHashRate
from .scrypt import ScryptHashRate
from .sha256 import SHA256HashRate
from .x11 import X11HashRate
class AlgoHashRate:
SHA256 = SHA256HashRate
SCRYPT = ScryptHashRate
KHEAVYHASH = KHeavyHashHashRate
KADENA = KadenaHashRate
HANDSHAKE = HandshakeHashRate
X11 = X11HashRate
BLAKE256 = Blake256HashRate
EAGLESONG = EaglesongHashRate
ETHASH = EtHashHashRate
EQUIHASH = EquihashHashRate

View File

@@ -1,62 +0,0 @@
from abc import ABC, abstractmethod
from pydantic import BaseModel
from typing_extensions import Self
from .unit.base import AlgoHashRateUnitType
class AlgoHashRateType(BaseModel, ABC):
unit: AlgoHashRateUnitType
rate: float
@abstractmethod
def into(self, other: "AlgoHashRateUnitType"):
pass
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, self.__class__):
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, self.__class__):
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, self.__class__):
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, self.__class__):
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, self.__class__):
return self.__class__(
rate=self.rate * other.into(self.unit).rate, unit=self.unit
)
return self.__class__(rate=self.rate * other, unit=self.unit)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.blake256 import Blake256Unit
from .unit import HashUnit
class Blake256HashRate(AlgoHashRateType):
rate: float
unit: Blake256Unit = HashUnit.BLAKE256.default
def into(self, other: Blake256Unit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.eaglesong import EaglesongUnit
from .unit import HashUnit
class EaglesongHashRate(AlgoHashRateType):
rate: float
unit: EaglesongUnit = HashUnit.EAGLESONG.default
def into(self, other: EaglesongUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.equihash import EquihashUnit
from .unit import HashUnit
class EquihashHashRate(AlgoHashRateType):
rate: float
unit: EquihashUnit = HashUnit.ETHASH.default
def into(self, other: EquihashUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.ethash import EtHashUnit
from .unit import HashUnit
class EtHashHashRate(AlgoHashRateType):
rate: float
unit: EtHashUnit = HashUnit.ETHASH.default
def into(self, other: EtHashUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.handshake import HandshakeUnit
from .unit import HashUnit
class HandshakeHashRate(AlgoHashRateType):
rate: float
unit: HandshakeUnit = HashUnit.HANDSHAKE.default
def into(self, other: HandshakeUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.kadena import KadenaUnit
from .unit import HashUnit
class KadenaHashRate(AlgoHashRateType):
rate: float
unit: KadenaUnit = HashUnit.KADENA.default
def into(self, other: KadenaUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.kheavyhash import KHeavyHashUnit
from .unit import HashUnit
class KHeavyHashHashRate(AlgoHashRateType):
rate: float
unit: KHeavyHashUnit = HashUnit.KHEAVYHASH.default
def into(self, other: KHeavyHashUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit
from .unit import HashUnit
class ScryptHashRate(AlgoHashRateType):
rate: float
unit: ScryptUnit = HashUnit.SCRYPT.default
def into(self, other: ScryptUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.sha256 import SHA256Unit
from .unit import HashUnit
class SHA256HashRate(AlgoHashRateType):
rate: float
unit: SHA256Unit = HashUnit.SHA256.default
def into(self, other: SHA256Unit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,23 +0,0 @@
from .blake256 import Blake256Unit
from .eaglesong import EaglesongUnit
from .equihash import EquihashUnit
from .ethash import EtHashUnit
from .handshake import HandshakeUnit
from .kadena import KadenaUnit
from .kheavyhash import KHeavyHashUnit
from .scrypt import ScryptUnit
from .sha256 import SHA256Unit
from .x11 import X11Unit
class HashUnit:
SHA256 = SHA256Unit
SCRYPT = ScryptUnit
KHEAVYHASH = KHeavyHashUnit
KADENA = KadenaUnit
HANDSHAKE = HandshakeUnit
X11 = X11Unit
BLAKE256 = Blake256Unit
EAGLESONG = EaglesongUnit
ETHASH = EtHashUnit
EQUIHASH = EquihashUnit

View File

@@ -1,55 +0,0 @@
from enum import IntEnum
class AlgoHashRateUnitType(IntEnum):
H: int
KH: int
MH: int
GH: int
TH: int
PH: int
EH: int
ZH: int
default: int
def __str__(self):
if self.value == self.H:
return "H/s"
if self.value == self.KH:
return "KH/s"
if self.value == self.MH:
return "MH/s"
if self.value == self.GH:
return "GH/s"
if self.value == self.TH:
return "TH/s"
if self.value == self.PH:
return "PH/s"
if self.value == self.EH:
return "EH/s"
if self.value == self.ZH:
return "ZH/s"
@classmethod
def from_str(cls, value: str):
if value == "H":
return cls.H
elif value == "KH":
return cls.KH
elif value == "MH":
return cls.MH
elif value == "GH":
return cls.GH
elif value == "TH":
return cls.TH
elif value == "PH":
return cls.PH
elif value == "EH":
return cls.EH
elif value == "ZH":
return cls.ZH
return cls.default
def __repr__(self):
return str(self)

View File

@@ -1,16 +0,0 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class Blake256Unit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -1,16 +0,0 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class EaglesongUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -1,34 +0,0 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class EquihashUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = KH
def __str__(self):
if self.value == self.H:
return "Sol/s"
if self.value == self.KH:
return "KSol/s"
if self.value == self.MH:
return "MSol/s"
if self.value == self.GH:
return "GSol/s"
if self.value == self.TH:
return "TSol/s"
if self.value == self.PH:
return "PSol/s"
if self.value == self.EH:
return "ESol/s"
if self.value == self.ZH:
return "ZSol/s"

View File

@@ -1,16 +0,0 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class EtHashUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = MH

View File

@@ -1,16 +0,0 @@
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

View File

@@ -1,16 +0,0 @@
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

View File

@@ -1,16 +0,0 @@
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

View File

@@ -1,16 +0,0 @@
from __future__ import annotations
from enum import IntEnum
class ScryptUnit(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 = GH

View File

@@ -1,16 +0,0 @@
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

View File

@@ -1,16 +0,0 @@
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

View File

@@ -1,18 +0,0 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.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
)

View File

@@ -1,13 +0,0 @@
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"

View File

@@ -1,13 +0,0 @@
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"

View File

@@ -1,12 +0,0 @@
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"

View File

@@ -1,13 +0,0 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import SHA256HashRate
from .hashrate.unit import SHA256Unit
# make this json serializable
class SHA256Algo(MinerAlgoType):
hashrate: type[SHA256HashRate] = SHA256HashRate
unit: type[SHA256Unit] = SHA256Unit
name = "SHA256"

View File

@@ -1,13 +0,0 @@
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"

View File

@@ -1,30 +0,0 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from enum import Enum
class MinerFirmware(str, Enum):
STOCK = "Stock"
BRAIINS_OS = "BOS+"
VNISH = "VNish"
EPIC = "ePIC"
HIVEON = "Hive"
LUXOS = "LuxOS"
MARATHON = "MaraFW"
def __str__(self):
return self.value

View File

@@ -1,33 +0,0 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from enum import Enum
class MinerMake(str, Enum):
WHATSMINER = "WhatsMiner"
ANTMINER = "AntMiner"
AVALONMINER = "AvalonMiner"
INNOSILICON = "Innosilicon"
GOLDSHELL = "Goldshell"
AURADINE = "Auradine"
EPIC = "ePIC"
BITAXE = "BitAxe"
ICERIVER = "IceRiver"
HAMMER = "Hammer"
def __str__(self):
return self.value

View File

@@ -1,390 +0,0 @@
from enum import Enum
class MinerModelType(str, Enum):
pass
class AntminerModels(MinerModelType):
D3 = "D3"
HS3 = "HS3"
L3Plus = "L3+"
KA3 = "KA3"
KS3 = "KS3"
DR5 = "DR5"
KS5 = "KS5"
L7 = "L7"
K7 = "K7"
D7 = "D7"
E9Pro = "E9Pro"
S9 = "S9"
S9i = "S9i"
S9j = "S9j"
T9 = "T9"
D9 = "D9"
Z15 = "Z15"
Z15Pro = "Z15 Pro"
S17 = "S17"
S17Plus = "S17+"
S17Pro = "S17 Pro"
S17e = "S17e"
T17 = "T17"
T17Plus = "T17+"
T17e = "T17e"
S19 = "S19"
S19NoPIC = "S19 No PIC"
S19L = "S19L"
S19Pro = "S19 Pro"
S19j = "S19j"
S19i = "S19i"
S19Plus = "S19+"
S19jNoPIC = "S19j No PIC"
S19ProPlus = "S19 Pro+"
S19jPro = "S19j Pro"
S19jProNoPIC = "S19j Pro No PIC"
S19jProPlus = "S19j Pro+"
S19jProPlusNoPIC = "S19j Pro+ No PIC"
S19XP = "S19 XP"
S19a = "S19a"
S19aPro = "S19a Pro"
S19Hydro = "S19 Hydro"
S19ProHydro = "S19 Pro Hydro"
S19ProPlusHydro = "S19 Pro+ Hydro"
S19KPro = "S19K Pro"
S19kPro = "S19k Pro"
S19kProNoPIC = "S19k Pro No PIC"
T19 = "T19"
S21 = "S21"
S21Pro = "S21 Pro"
T21 = "T21"
def __str__(self):
return self.value
class WhatsminerModels(MinerModelType):
M20V10 = "M20 V10"
M20SV10 = "M20S V10"
M20SV20 = "M20S V20"
M20SV30 = "M20S V30"
M20PV10 = "M20P V10"
M20PV30 = "M20P V30"
M20SPlusV30 = "M20S+ V30"
M21V10 = "M21 V10"
M21SV20 = "M21S V20"
M21SV60 = "M21S V60"
M21SV70 = "M21S V70"
M21SPlusV20 = "M21S+ V20"
M29V10 = "M29 V10"
M30V10 = "M30 V10"
M30V20 = "M30 V20"
M30KV10 = "M30K V10"
M30LV10 = "M30L V10"
M30SV10 = "M30S V10"
M30SV20 = "M30S V20"
M30SV30 = "M30S V30"
M30SV40 = "M30S V40"
M30SV50 = "M30S V50"
M30SV60 = "M30S V60"
M30SV70 = "M30S V70"
M30SV80 = "M30S V80"
M30SVE10 = "M30S VE10"
M30SVE20 = "M30S VE20"
M30SVE30 = "M30S VE30"
M30SVE40 = "M30S VE40"
M30SVE50 = "M30S VE50"
M30SVE60 = "M30S VE60"
M30SVE70 = "M30S VE70"
M30SVF10 = "M30S VF10"
M30SVF20 = "M30S VF20"
M30SVF30 = "M30S VF30"
M30SVG10 = "M30S VG10"
M30SVG20 = "M30S VG20"
M30SVG30 = "M30S VG30"
M30SVG40 = "M30S VG40"
M30SVH10 = "M30S VH10"
M30SVH20 = "M30S VH20"
M30SVH30 = "M30S VH30"
M30SVH40 = "M30S VH40"
M30SVH50 = "M30S VH50"
M30SVH60 = "M30S VH60"
M30SVI20 = "M30S VI20"
M30SPlusV10 = "M30S+ V10"
M30SPlusV20 = "M30S+ V20"
M30SPlusV30 = "M30S+ V30"
M30SPlusV40 = "M30S+ V40"
M30SPlusV50 = "M30S+ V50"
M30SPlusV60 = "M30S+ V60"
M30SPlusV70 = "M30S+ V70"
M30SPlusV80 = "M30S+ V80"
M30SPlusV90 = "M30S+ V90"
M30SPlusV100 = "M30S+ V100"
M30SPlusVE30 = "M30S+ VE30"
M30SPlusVE40 = "M30S+ VE40"
M30SPlusVE50 = "M30S+ VE50"
M30SPlusVE60 = "M30S+ VE60"
M30SPlusVE70 = "M30S+ VE70"
M30SPlusVE80 = "M30S+ VE80"
M30SPlusVE90 = "M30S+ VE90"
M30SPlusVE100 = "M30S+ VE100"
M30SPlusVF20 = "M30S+ VF20"
M30SPlusVF30 = "M30S+ VF30"
M30SPlusVG20 = "M30S+ VG20"
M30SPlusVG30 = "M30S+ VG30"
M30SPlusVG40 = "M30S+ VG40"
M30SPlusVG50 = "M30S+ VG50"
M30SPlusVG60 = "M30S+ VG60"
M30SPlusVH10 = "M30S+ VH10"
M30SPlusVH20 = "M30S+ VH20"
M30SPlusVH30 = "M30S+ VH30"
M30SPlusVH40 = "M30S+ VH40"
M30SPlusVH50 = "M30S+ VH50"
M30SPlusVH60 = "M30S+ VH60"
M30SPlusPlusV10 = "M30S++ V10"
M30SPlusPlusV20 = "M30S++ V20"
M30SPlusPlusVE30 = "M30S++ VE30"
M30SPlusPlusVE40 = "M30S++ VE40"
M30SPlusPlusVE50 = "M30S++ VE50"
M30SPlusPlusVF40 = "M30S++ VF40"
M30SPlusPlusVG30 = "M30S++ VG30"
M30SPlusPlusVG40 = "M30S++ VG40"
M30SPlusPlusVG50 = "M30S++ VG50"
M30SPlusPlusVH10 = "M30S++ VH10"
M30SPlusPlusVH20 = "M30S++ VH20"
M30SPlusPlusVH30 = "M30S++ VH30"
M30SPlusPlusVH40 = "M30S++ VH40"
M30SPlusPlusVH50 = "M30S++ VH50"
M30SPlusPlusVH60 = "M30S++ VH60"
M30SPlusPlusVH70 = "M30S++ VH70"
M30SPlusPlusVH80 = "M30S++ VH80"
M30SPlusPlusVH90 = "M30S++ VH90"
M30SPlusPlusVH100 = "M30S++ VH100"
M30SPlusPlusVJ20 = "M30S++ VJ20"
M30SPlusPlusVJ30 = "M30S++ VJ30"
M31V10 = "M31 V10"
M31V20 = "M31 V20"
M31HV10 = "M31H V10"
M31HV40 = "M31H V40"
M31LV10 = "M30L V10"
M31SV10 = "M31S V10"
M31SV20 = "M31S V20"
M31SV30 = "M31S V30"
M31SV40 = "M31S V40"
M31SV50 = "M31S V50"
M31SV60 = "M31S V60"
M31SV70 = "M31S V70"
M31SV80 = "M31S V80"
M31SV90 = "M31S V90"
M31SVE10 = "M31S VE10"
M31SVE20 = "M31S VE20"
M31SVE30 = "M31S VE30"
M31SEV10 = "M31SE V10"
M31SEV20 = "M31SE V20"
M31SEV30 = "M31SE V30"
M31SPlusV10 = "M31S+ V10"
M31SPlusV20 = "M31S+ V20"
M31SPlusV30 = "M31S+ V30"
M31SPlusV40 = "M31S+ V40"
M31SPlusV50 = "M31S+ V50"
M31SPlusV60 = "M31S+ V60"
M31SPlusV80 = "M31S+ V80"
M31SPlusV90 = "M31S+ V90"
M31SPlusV100 = "M31S+ V100"
M31SPlusVE10 = "M31S+ VE10"
M31SPlusVE20 = "M31S+ VE20"
M31SPlusVE30 = "M31S+ VE30"
M31SPlusVE40 = "M31S+ VE40"
M31SPlusVE50 = "M31S+ VE50"
M31SPlusVE60 = "M31S+ VE60"
M31SPlusVE80 = "M31S+ VE80"
M31SPlusVF20 = "M31S+ VF20"
M31SPlusVF30 = "M31S+ VF30"
M31SPlusVG20 = "M31S+ VG20"
M31SPlusVG30 = "M31S+ VG30"
M32V10 = "M32 V10"
M32V20 = "M32 V20"
M32S = "M32S"
M33V10 = "M33 V10"
M33V20 = "M33 V20"
M33V30 = "M33 V30"
M33SVG30 = "M33S VG30"
M33SPlusVG20 = "M33S+ VG20"
M33SPlusVH20 = "M33S+ VH20"
M33SPlusVH30 = "M33S+ VH30"
M33SPlusPlusVH20 = "M33S++ VH20"
M33SPlusPlusVH30 = "M33S++ VH30"
M33SPlusPlusVG40 = "M33S++ VG40"
M34SPlusVE10 = "M34S+ VE10"
M36SVE10 = "M36S VE10"
M36SPlusVG30 = "M36S+ VG30"
M36SPlusPlusVH30 = "M36S++ VH30"
M39V10 = "M39 V10"
M39V20 = "M39 V20"
M39V30 = "M39 V30"
M50VE30 = "M50 VE30"
M50VG30 = "M50 VG30"
M50VH10 = "M50 VH10"
M50VH20 = "M50 VH20"
M50VH30 = "M50 VH30"
M50VH40 = "M50 VH40"
M50VH50 = "M50 VH50"
M50VH60 = "M50 VH60"
M50VH70 = "M50 VH70"
M50VH80 = "M50 VH80"
M50VH90 = "M50 VH90"
M50VJ10 = "M50 VJ10"
M50VJ20 = "M50 VJ20"
M50VJ30 = "M50 VJ30"
M50SVJ10 = "M50S VJ10"
M50SVJ20 = "M50S VJ20"
M50SVJ30 = "M50S VJ30"
M50SVH10 = "M50S VH10"
M50SVH20 = "M50S VH20"
M50SVH30 = "M50S VH30"
M50SVH40 = "M50S VH40"
M50SVH50 = "M50S VH50"
M50SPlusVH30 = "M50S+ VH30"
M50SPlusVH40 = "M50S+ VH40"
M50SPlusVJ30 = "M50S+ VJ30"
M50SPlusVK20 = "M50S+ VK20"
M50SPlusPlusVK10 = "M50S++ VK10"
M50SPlusPlusVK20 = "M50S++ VK20"
M50SPlusPlusVK30 = "M50S++ VK30"
M53VH30 = "M53 VH30"
M53SVH30 = "M53S VH30"
M53SVJ40 = "M53S VJ40"
M53SPlusVJ30 = "M53S+ VJ30"
M53SPlusPlusVK10 = "M53S++ VK10"
M56VH30 = "M56 VH30"
M56SVH30 = "M56S VH30"
M56SPlusVJ30 = "M56S+ VJ30"
M59VH30 = "M59 VH30"
M60VK10 = "M60 VK10"
M60VK20 = "M60 VK20"
M60VK30 = "M60 VK30"
M60VK40 = "M60 VK40"
M60SVK10 = "M60S VK10"
M60SVK20 = "M60S VK20"
M60SVK30 = "M60S VK30"
M60SVK40 = "M60S VK40"
M63VK10 = "M63 VK10"
M63VK20 = "M63 VK20"
M63VK30 = "M63 VK30"
M63SVK10 = "M63S VK10"
M63SVK20 = "M63S VK20"
M63SVK30 = "M63S VK30"
M66VK20 = "M66 VK20"
M66VK30 = "M66 VK30"
M66SVK20 = "M66S VK20"
M66SVK30 = "M66S VK30"
M66SVK40 = "M66S VK40"
def __str__(self):
return self.value
class AvalonminerModels(MinerModelType):
Avalon721 = "Avalon 721"
Avalon741 = "Avalon 741"
Avalon761 = "Avalon 761"
Avalon821 = "Avalon 821"
Avalon841 = "Avalon 841"
Avalon851 = "Avalon 851"
Avalon921 = "Avalon 921"
Avalon1026 = "Avalon 1026"
Avalon1047 = "Avalon 1047"
Avalon1066 = "Avalon 1066"
Avalon1166Pro = "Avalon 1166 Pro"
Avalon1246 = "Avalon 1246"
AvalonNano3 = "Avalon Nano 3"
def __str__(self):
return self.value
class InnosiliconModels(MinerModelType):
T3HPlus = "T3H+"
A10X = "A10X"
A11 = "A11"
A11MX = "A11MX"
def __str__(self):
return self.value
class GoldshellModels(MinerModelType):
CK5 = "CK5"
HS5 = "HS5"
KD5 = "KD5"
KDMax = "KD Max"
KDBoxII = "KD Box II"
KDBoxPro = "KD Box Pro"
def __str__(self):
return self.value
class ePICModels(MinerModelType):
BM520i = "BlockMiner 520i"
BM720i = "BlockMiner 720i"
def __str__(self):
return self.value
class AuradineModels(MinerModelType):
AT1500 = "AT1500"
AT2860 = "AT2860"
AT2880 = "AT2880"
AI2500 = "AI2500"
AI3680 = "AI3680"
AD2500 = "AD2500"
AD3500 = "AD3500"
def __str__(self):
return self.value
class BitAxeModels(MinerModelType):
BM1366 = "Ultra"
BM1368 = "Supra"
BM1397 = "Max"
BM1370 = "Gamma"
def __str__(self):
return self.value
class IceRiverModels(MinerModelType):
KS0 = "KS0"
KS1 = "KS1"
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):
return self.value
class MinerModel:
ANTMINER = AntminerModels
WHATSMINER = WhatsminerModels
AVALONMINER = AvalonminerModels
INNOSILICON = InnosiliconModels
GOLDSHELL = GoldshellModels
AURADINE = AuradineModels
EPIC = ePICModels
BITAXE = BitAxeModels
ICERIVER = IceRiverModels
HAMMER = HammerModels

Some files were not shown because too many files have changed in this diff Show More