Compare commits

...

117 Commits

Author SHA1 Message Date
Brett Rowan
5f2f6e01da version: bump version number 2025-07-10 10:56:42 -06:00
Brody
41b4c23d45 Fix API Issue with Bitaxe Miners (#352)
* added checks to identify which field is None.

* better logging

* if asicCount is None, try to find it in /system/asic

* ran pre-commit
2025-07-10 10:56:22 -06:00
Brett Rowan
b4687f18fd version: bump version number 2025-05-01 15:15:42 -06:00
Brett Rowan
2437421005 feature: add support for a bunch of BOS and VNish S21 models. 2025-05-01 15:15:18 -06:00
Ryan Heideman
40ebc2773f Retry and Timeout Changes for espminer (#340)
* Retry and Timeout Fixes for espminer

* Build check fix
2025-05-01 15:07:58 -06:00
Art
b8ae238d23 Fix incorrect computation of active_preset for Antminer VNish. See #338. (#339)
* Fix incorrect computation of active_preset for Antminer VNish. See #338.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* #338 renamed perf_summary to web_perf_summary for clarity and consistency. New API call is done in backward compatibility manner. Data is also retrieved safely

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-01 15:07:11 -06:00
Brett Rowan
2920639b70 version: bump version number 2025-04-27 11:01:10 -06:00
Brett Rowan
bd9144b3de requirements: bump dependency versions 2025-04-27 11:01:10 -06:00
SKART1
8f7a67d4dc Fix efficiency_fract property to correctly return computed value. See #334. 2025-04-19 08:28:44 -06:00
Brett Rowan
a62bea33a7 docs: update docs 2025-04-18 16:15:31 -06:00
pre-commit-ci[bot]
406bcd179c [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-04-18 16:12:55 -06:00
Ryan Heideman
aa87ef7d71 nano3s fixes 2025-04-18 16:12:55 -06:00
SKART1
ec88fbf6aa Fix #334 efficiency calculation and add computed fields 2025-04-18 16:12:44 -06:00
pre-commit-ci[bot]
e23c86a944 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/python-poetry/poetry: 2.1.1 → 2.1.2](https://github.com/python-poetry/poetry/compare/2.1.1...2.1.2)
2025-04-16 08:41:30 -06:00
Brett Rowan
b81276cb19 version: bump version number 2025-04-16 08:40:36 -06:00
Adrian
8dcc72b1bb T21 working on LuxOS (#331) 2025-04-14 12:55:54 -06:00
Timur Taipov
540572356f feature: add support for S21+ hydro (#326) 2025-04-14 09:15:06 -06:00
Ryan Heideman
83035a869b feature: Add Support for Avalon Nano 3s (#329) 2025-04-12 15:34:45 -06:00
Brett Rowan
4c104a59ff version: bump version number 2025-04-01 09:24:20 -06:00
Brett Rowan
e708ae3728 bug: remove MSKminer web identification 2025-04-01 09:23:51 -06:00
Wilfred Allyn
a4352816ee bug: pass atmset param as bool 2025-04-01 07:48:42 -06:00
Will Jackson
336bd9c002 bug: update set_dps configuration to handle optional power and hashrate parameters (#319)
* bug: update set_dps configuration to handle optional power and hashrate parameters
2025-03-27 11:09:47 -06:00
Wilfred Allyn
e3c917efde feature: set supports_autotuning True for luxminer 2025-03-26 08:06:38 -06:00
Brett Rowan
4d71012ed6 version: bump version number
Closes: #310
2025-03-20 10:46:30 -06:00
Will Jackson
1acdba8ae0 bug: fix set_dps configuration to handle power target parameters correctly (#318)
* bug: fix set_dps configuration to handle power target parameters correctly

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-20 10:45:48 -06:00
Brett Rowan
5567f26c03 version: bump version number
Closes: #316, #310
2025-03-20 09:20:51 -06:00
Brett Rowan
0027485582 bug: fix set_dps tuple
cc: #310
2025-03-20 09:20:23 -06:00
Brett Rowan
c9bcb7bab6 feature: add support for S21+
cc: #316
2025-03-19 13:28:03 -06:00
John-Paul Compagnone
0ee261930e epic: remove guard on expected_hashboards 2025-03-18 09:35:13 -06:00
John-Paul Compagnone
3cfc8dded9 epic: remove v1 from ELITE miner 2025-03-18 09:35:13 -06:00
John-Paul Compagnone
655cf6d0ac epic: add BlockMiner eLITE, S19k Pro Dualie 2025-03-18 09:35:13 -06:00
Brett Rowan
640dc6d8c2 version: bump version number 2025-03-18 09:00:40 -06:00
Brett Rowan
a572fedb4d bug: fix some cases where bosminer config could be invalid 2025-03-18 09:00:16 -06:00
Brett Rowan
6c46a7cd71 bug: handle cases where hashboards and fans can be None with unknown types 2025-03-18 08:18:34 -06:00
Brett Rowan
49f42172da version: bump version number 2025-03-17 14:19:47 -06:00
Brett Rowan
83be80e4bc bug: handle connection errors in grpc for boser 2025-03-17 14:19:28 -06:00
Brett Rowan
0be9e9d519 feature: add fault_light to influx 2025-03-17 14:17:37 -06:00
Upstream Data
4694c0f774 bug: fix error code handling in list 2025-03-06 11:45:38 -07:00
Brett Rowan
11012b310b bug: fix delimiter handling 2025-03-04 21:27:28 -07:00
Brett Rowan
8852fab3ee feature: allow the user to pass a delimiter to as_influx 2025-03-04 20:14:55 -07:00
Brett Rowan
ec1b2ca162 bug: fix bad " " handling in influx 2025-03-04 19:58:44 -07:00
Upstream Data
1f9d4c8c10 bug: swap to lower case booleans 2025-03-04 19:51:48 -07:00
Upstream Data
5bb04b2af4 refactor: round hashrate to 2 decimals for influx 2025-03-04 19:51:48 -07:00
Upstream Data
2e192a1536 feature: update support for influxdb line protocol 2025-03-04 19:51:48 -07:00
Brett Rowan
4da8044bc7 bug: fix bad type hint 2025-03-04 19:51:37 -07:00
Brett Rowan
76078e4d0e bug: fix wattage limit parsing on luxos 2025-03-04 19:51:13 -07:00
Upstream Data
276a476fab bug: fix json decode error possibility with avalon nanos 2025-03-03 15:04:22 -07:00
pre-commit-ci[bot]
e0abed4f93 [pre-commit.ci] pre-commit autoupdate (#307)
updates:
- [github.com/pycqa/isort: 6.0.0 → 6.0.1](https://github.com/pycqa/isort/compare/6.0.0...6.0.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-03 14:56:47 -07:00
Ganey
4a67bd5d99 feat: add powerlimit to avalon nano (#302)
* feat: add powerlimit to avalon nano

add power limit to avalon nano

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-02-26 15:23:18 -07:00
pre-commit-ci[bot]
eb48d04939 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/python-poetry/poetry: 2.0.1 → 2.1.1](https://github.com/python-poetry/poetry/compare/2.0.1...2.1.1)
2025-02-18 08:44:43 -07:00
Upstream Data
c4697728e4 version: bump version number 2025-02-13 15:38:53 -07:00
Upstream Data
de64172073 feature: add support for Hiveon S19kPro 2025-02-13 15:38:28 -07:00
Upstream Data
4d7a13433b version: bump version number 2025-02-12 16:08:23 -07:00
Adrian
e14a4791b2 it turns out there are 3 hashboards per miner (#293) 2025-02-12 16:07:46 -07:00
Upstream Data
0e76d4550b version: bump version number 2025-02-11 15:47:56 -07:00
Upstream Data
2d424025e9 feature: add MAC address to mskminer 2025-02-11 15:47:37 -07:00
Upstream Data
bf4903ce4b refactor: remove some unused imports 2025-02-11 09:38:31 -07:00
Upstream Data
4f7f6bf045 version: bump version number 2025-02-11 09:34:57 -07:00
Upstream Data
824890ec97 feature: add very basic support for MSKminer 2025-02-11 09:34:23 -07:00
Upstream Data
ce9d7ffb0f bug: fix issue with vnish preset parsing on some miners 2025-02-11 09:00:59 -07:00
Upstream Data
183b4934c1 refactor: remote unused import in test 2025-02-06 11:44:15 -07:00
Upstream Data
3d2b260b17 version: bump version number 2025-02-06 11:42:33 -07:00
Upstream Data
f88c1734eb bug: fix some issues with avalon 1566, and add tests 2025-02-06 11:42:17 -07:00
Upstream Data
b897ca8363 version: bump version number 2025-02-06 11:33:08 -07:00
Upstream Data
dba341fdae feature: add support for avalon 1566 2025-02-06 11:32:45 -07:00
Upstream Data
837794bd57 version: bump version number 2025-02-05 10:45:56 -07:00
Upstream Data
36d16c7235 feature: add support for elphapex configs 2025-02-05 10:45:35 -07:00
Upstream Data
7797023689 feature: add support for elphapex pools 2025-02-05 08:28:28 -07:00
Upstream Data
1021f200ec version: bump version number 2025-02-05 08:07:35 -07:00
Upstream Data
197d6568e3 bug: fix DG1+ naming 2025-02-05 08:07:14 -07:00
Upstream Data
71c8905674 version: bump version number 2025-02-04 16:00:18 -07:00
Upstream Data
15c3806fbf refactor: cleanup some imports 2025-02-04 15:59:32 -07:00
Upstream Data
a5c42c9c2b feature: add support for Elphapex DG1+ 2025-02-04 15:59:32 -07:00
Upstream Data
90d8a795e6 version: bump version number 2025-02-03 09:39:49 -07:00
Upstream Data
07cf1b134c feature: add support for vnish error codes 2025-02-03 09:39:30 -07:00
Upstream Data
53e1f33fa6 version: bump version number 2025-02-03 09:32:44 -07:00
Upstream Data
16ab2bd4e3 feature: add support for luckyminer LV07 2025-02-03 09:32:16 -07:00
Upstream Data
de728f12d2 version: bump version number 2025-02-03 09:27:43 -07:00
Upstream Data
8092e12dfb feature: add support for S19X88 Hiveon 2025-02-03 09:27:18 -07:00
b-rowan
e70c3e9f79 version: bump version number 2025-01-30 17:56:47 -07:00
b-rowan
39a97f2914 feature: add mac address support for avalon nano 3 2025-01-30 17:56:16 -07:00
b-rowan
35af74ad1a version: bump version number 2025-01-30 07:14:50 -07:00
Erik Olof Gunnar Andersson
15fa91fb98 bug: don’t timeout forever on _socket_ping (#288)
* Don't timeout forever on _socket_ping

* Use factory_get_timeout to configure number of allowed retries

* Add warning log
2025-01-30 07:05:29 -07:00
James Hilliard
084987a3e1 bug: Add cryptography direct dependency (#286) 2025-01-29 10:01:10 -07:00
b-rowan
e93cc77a58 ci: update pre-commit hooks 2025-01-28 21:29:43 -07:00
b-rowan
788d43c51c version: bump version number 2025-01-28 21:27:17 -07:00
Adrian
74c22b82ce bug: add miner algo type for S21 Hydro (#285) 2025-01-28 21:20:39 -07:00
Upstream Data
1f46ce1b9a version: bump version number 2025-01-28 09:36:56 -07:00
Adrian
7964336a0c feature: add support for S21 Hydro (#283) 2025-01-28 09:36:15 -07:00
Upstream Data
a24fc07c2a ci: update pre-commit to run docs generation when committing. 2025-01-28 08:51:13 -07:00
Upstream Data
4c64481d3b bug: do not run unittest when running in precommit.ci 2025-01-28 08:33:02 -07:00
Upstream Data
66fb5834f0 version: bump version number 2025-01-23 13:37:28 -07:00
Upstream Data
db05cc1d97 feature: add support for S21 Pro BOS 2025-01-23 13:37:04 -07:00
Brett Rowan
418e3ce26e Merge pull request #281 from UpstreamData/fix_hammer_pass 2025-01-23 07:42:20 -07:00
John-Paul Compagnone
5a0bb11a44 fix hammer default password 2025-01-23 08:35:37 -05:00
Upstream Data
39f9d087db version: bump version number 2025-01-20 09:27:40 -07:00
Upstream Data
067a376f94 feature: add support for luckyminer LV08 2025-01-20 09:27:16 -07:00
Upstream Data
a5d6e122f9 version: bump version number 2025-01-20 09:01:04 -07:00
Upstream Data
b160fd75ba refactor: reformat some files 2025-01-20 09:00:45 -07:00
Upstream Data
d1007d3ae8 version: bump version number 2025-01-20 08:58:14 -07:00
Upstream Data
33803e89e2 feature: add support for Vnish S19i
Fixes: #279
2025-01-20 08:21:24 -07:00
Upstream Data
10a44b9877 bug: fix invlaid import in luxminer.py 2025-01-20 08:21:12 -07:00
pre-commit-ci[bot]
bbd883f639 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-01-19 08:18:27 -07:00
Wilfred Allyn
8e2ad478e9 refactor: simplify get_wattage_limit for luxos 2025-01-19 08:18:27 -07:00
Wilfred Allyn
957981a9c6 feature: get active preset from luxos 2025-01-19 08:18:27 -07:00
Wilfred Allyn
13a67dfdd1 feature: add _get_wattage_limit for luxos 2025-01-19 08:18:27 -07:00
Wilfred Allyn
e86f2b62c5 docs: fix issues for docs warnings 2025-01-18 08:03:11 -07:00
Wilfred Allyn
88b4d2cac3 docs: add instructions for building docs locally 2025-01-17 09:31:36 -07:00
Upstream Data
5842ef3d97 version: bump version number 2025-01-16 11:02:57 -07:00
Upstream Data
339a689267 bug: fix possible None value from miner type when parsing hammer miners 2025-01-16 11:02:39 -07:00
Brett Rowan
a0764806c4 version: bump version number 2025-01-15 22:23:41 -07:00
Brett Rowan
6eec0f6d44 bug: fix a few bad type hints in config models 2025-01-15 22:22:53 -07:00
Upstream Data
0854f7833c docs: update supported miners list and generation tool to use unique 2025-01-15 10:56:25 -07:00
Upstream Data
b6edc85679 feature: add support for some weird antminer submodel namings for S21 2025-01-15 09:47:14 -07:00
Upstream Data
ff11ebc304 bug: fix antminer KS5 using iceriver backend 2025-01-15 08:25:02 -07:00
Wilfred Allyn
f3681f1aa5 feature: add support for Vnish S19j Pro A 2025-01-15 07:36:29 -07:00
Wilfred Allyn
1a7411edb3 feature: add support for Vnish T21 and update docs 2025-01-15 07:34:47 -07:00
139 changed files with 5522 additions and 1383 deletions

View File

@@ -1,7 +1,15 @@
ci: ci:
skip: skip:
- poetry-lock
- unittest - unittest
- generate-docs
repos: repos:
- repo: https://github.com/python-poetry/poetry
rev: 2.1.2
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-install
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v5.0.0
hooks: hooks:
@@ -15,11 +23,11 @@ repos:
exclude: ^mkdocs\.yml$ exclude: ^mkdocs\.yml$
- id: check-added-large-files - id: check-added-large-files
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 24.10.0 rev: 25.1.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
rev: 5.13.2 rev: 6.0.1
hooks: hooks:
- id: isort - id: isort
name: isort (python) name: isort (python)
@@ -30,6 +38,12 @@ repos:
name: unittest name: unittest
entry: python -m unittest discover entry: python -m unittest discover
language: system language: system
'types': [python] types: [ python ]
args: ["-p '*test.py'"] # Probably this option is absolutely not needed. args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
pass_filenames: false pass_filenames: false
- id: generate-docs
name: generate-docs
entry: python docs/generate_miners.py
language: system
types: [ python ]
pass_filenames: false

View File

@@ -23,7 +23,7 @@ Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with
## Installation ## Installation
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include: It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include:
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default - [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default. Use version 2.0+
``` ```
poetry install poetry install
@@ -50,6 +50,13 @@ poetry install --with dev
pre-commit install pre-commit install
``` ```
##### Building Documentation Locally
```
poetry install --with docs
python docs/generate_miners.py
poetry run mkdocs serve
```
--- ---
## Getting started ## Getting started

View File

@@ -2,6 +2,7 @@ import asyncio
import importlib import importlib
import os import os
import warnings import warnings
from pathlib import Path
from pyasic.miners.factory import MINER_CLASSES, MinerTypes from pyasic.miners.factory import MINER_CLASSES, MinerTypes
@@ -51,12 +52,19 @@ def backend_str(backend: MinerTypes) -> str:
return "Mara Firmware Miners" return "Mara Firmware Miners"
case MinerTypes.BITAXE: case MinerTypes.BITAXE:
return "Stock Firmware BitAxe Miners" return "Stock Firmware BitAxe Miners"
case MinerTypes.LUCKYMINER:
return "Stock Firmware Lucky Miners"
case MinerTypes.ICERIVER: case MinerTypes.ICERIVER:
return "Stock Firmware IceRiver Miners" return "Stock Firmware IceRiver Miners"
case MinerTypes.HAMMER: case MinerTypes.HAMMER:
return "Stock Firmware Hammer Miners" return "Stock Firmware Hammer Miners"
case MinerTypes.VOLCMINER: case MinerTypes.VOLCMINER:
return "Stock Firmware Volcminers" return "Stock Firmware Volcminers"
case MinerTypes.ELPHAPEX:
return "Stock Firmware Elphapex Miners"
case MinerTypes.MSKMINER:
return "MSKMiner Firmware Miners"
raise TypeError("Unknown miner backend, cannot generate docs")
def create_url_str(mtype: str): def create_url_str(mtype: str):
@@ -121,27 +129,28 @@ BACKEND_TYPE_CLOSER = """
</details>""" </details>"""
m_data = {} m_data = {}
done = []
for m in MINER_CLASSES: for m in MINER_CLASSES:
for t in MINER_CLASSES[m]: for t in sorted(MINER_CLASSES[m], key=lambda x: x or ""):
if t is not None: if t is not None and MINER_CLASSES[m][t] not in done:
miner = MINER_CLASSES[m][t] miner = MINER_CLASSES[m][t]
if make(miner) not in m_data: if make(miner) not in m_data:
m_data[make(miner)] = {} m_data[make(miner)] = {}
if model_type(miner) not in m_data[make(miner)]: if model_type(miner) not in m_data[make(miner)]:
m_data[make(miner)][model_type(miner)] = [] m_data[make(miner)][model_type(miner)] = []
m_data[make(miner)][model_type(miner)].append(miner) m_data[make(miner)][model_type(miner)].append(miner)
done.append(miner)
async def create_directory_structure(directory, data): def create_directory_structure(directory, data):
if not os.path.exists(directory): if not os.path.exists(directory):
os.makedirs(directory) os.makedirs(directory)
for key, value in data.items(): for key, value in data.items():
subdirectory = os.path.join(directory, key) subdirectory = os.path.join(directory, key)
if isinstance(value, dict): if isinstance(value, dict):
await create_directory_structure(subdirectory, value) create_directory_structure(subdirectory, value)
elif isinstance(value, list): elif isinstance(value, list):
file_path = os.path.join(subdirectory + ".md") file_path = os.path.join(subdirectory + ".md")
@@ -162,7 +171,7 @@ async def create_directory_structure(directory, data):
) )
async def create_supported_types(directory): def create_supported_types(directory):
with open(os.path.join(directory, "supported_types.md"), "w") as file: with open(os.path.join(directory, "supported_types.md"), "w") as file:
file.write(SUPPORTED_TYPES_HEADER) file.write(SUPPORTED_TYPES_HEADER)
for mback in MINER_CLASSES: for mback in MINER_CLASSES:
@@ -179,7 +188,7 @@ async def create_supported_types(directory):
for mtype in backend_types: for mtype in backend_types:
file.write(MINER_TYPE_HEADER.format(mtype)) file.write(MINER_TYPE_HEADER.format(mtype))
for minstance in backend_types[mtype]: for minstance in backend_types[mtype]:
model = await minstance("1.1.1.1").get_model() model = minstance("1.1.1.1").model
file.write( file.write(
MINER_DETAILS.format( MINER_DETAILS.format(
make(minstance), mtype, create_url_str(model), model make(minstance), mtype, create_url_str(model), model
@@ -189,6 +198,7 @@ async def create_supported_types(directory):
file.write(BACKEND_TYPE_CLOSER) file.write(BACKEND_TYPE_CLOSER)
root_directory = os.path.join(os.getcwd(), "miners") if __name__ == "__main__":
asyncio.run(create_directory_structure(root_directory, m_data)) root_directory = Path(__file__).parent.joinpath("miners")
asyncio.run(create_supported_types(root_directory)) create_directory_structure(root_directory, m_data)
create_supported_types(root_directory)

View File

@@ -14,19 +14,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S17+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17 Pro (Stock) ## S17 Pro (Stock)
- [ ] Shutdowns - [ ] Shutdowns
@@ -40,6 +27,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S17+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17e (Stock) ## S17e (Stock)
- [ ] Shutdowns - [ ] Shutdowns
@@ -105,19 +105,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S17+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17 Pro (BOS+) ## S17 Pro (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -131,6 +118,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S17+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17e (BOS+) ## S17e (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -183,19 +183,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S17+ (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17 Pro (VNish) ## S17 Pro (VNish)
- [x] Shutdowns - [x] Shutdowns
@@ -209,3 +196,16 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S17+ (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -14,14 +14,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19L (Stock) ## S19 Hydro (Stock)
- [x] Shutdowns - [x] Shutdowns
- [x] Power Modes - [x] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -40,136 +40,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19i (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j No PIC (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 XP (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Hydro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro Hydro (Stock) ## S19 Pro Hydro (Stock)
- [x] Shutdowns - [x] Shutdowns
@@ -196,14 +66,92 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19K Pro (Stock) ## S19 XP (Stock)
- [x] Shutdowns - [x] Shutdowns
- [x] Power Modes - [x] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19i (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -222,6 +170,58 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j No PIC (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19K Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19L (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (Stock) ## T19 (Stock)
- [x] Shutdowns - [x] Shutdowns
@@ -248,19 +248,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro (BOS+) ## S19 Pro (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -274,6 +261,45 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 Pro+ Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19ProPlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 XP (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (BOS+) ## S19a (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -313,19 +339,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j No PIC (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (BOS+) ## S19j Pro (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -359,19 +372,6 @@
- [x] Setpoints - [x] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python handler: python
options: options:
@@ -391,14 +391,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19k Pro No PIC (BOS+) ## S19j No PIC (BOS+)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [x] Setpoints - [x] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -417,32 +417,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro+ Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19ProPlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (BOS+) ## T19 (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -469,19 +443,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 No PIC (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro (VNish) ## S19 Pro (VNish)
- [x] Shutdowns - [x] Shutdowns
@@ -495,40 +456,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j (VNish) ## S19 Pro Hydro (VNish)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [x] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19j ::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -560,14 +495,40 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 Pro Hydro (VNish) ## S19i (VNish)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [x] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProHydro ::: pyasic.miners.antminer.vnish.X19.S19.VNishS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -586,6 +547,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 No PIC (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (VNish) ## T19 (VNish)
- [x] Shutdowns - [x] Shutdowns
@@ -625,6 +599,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (ePIC) ## S19j (ePIC)
- [x] Shutdowns - [x] Shutdowns
@@ -651,6 +638,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j Pro Dual (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19jProDual
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro+ (ePIC) ## S19j Pro+ (ePIC)
- [x] Shutdowns - [x] Shutdowns
@@ -677,40 +677,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (ePIC) ## S19k Pro Dual (ePIC)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP ::: pyasic.miners.antminer.epic.X19.S19.ePICS19kProDual
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro Dual (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19jProDual
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (Hive)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -729,11 +703,63 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j Pro (Hive)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19jPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19K Pro (Hive)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19kPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 No PIC (Hive)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 No PIC (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.mskminer.X19.S19.MSKMinerS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 (LuxOS) ## S19 (LuxOS)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19 ::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19
@@ -746,7 +772,7 @@
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19Pro ::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19Pro
@@ -755,11 +781,24 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (LuxOS)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (LuxOS) ## S19j Pro (LuxOS)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19jPro ::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19jPro
@@ -772,7 +811,7 @@
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19jProPlus ::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19jProPlus
@@ -785,7 +824,7 @@
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19kPro ::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19kPro
@@ -794,24 +833,11 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (LuxOS)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (LuxOS) ## T19 (LuxOS)
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X19.T19.LUXMinerT19 ::: pyasic.miners.antminer.luxos.X19.T19.LUXMinerT19
@@ -846,6 +872,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (MaraFW)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (MaraFW) ## S19j (MaraFW)
- [ ] Shutdowns - [ ] Shutdowns
@@ -859,19 +898,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19j No PIC (MaraFW)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (MaraFW) ## S19j Pro (MaraFW)
- [ ] Shutdowns - [ ] Shutdowns
@@ -885,14 +911,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S19 XP (MaraFW) ## S19j No PIC (MaraFW)
- [ ] Shutdowns - [ ] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19XP ::: pyasic.miners.antminer.marathon.X19.S19.MaraS19jNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false

View File

@@ -14,6 +14,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S21 Hydro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 Pro (Stock) ## S21 Pro (Stock)
- [x] Shutdowns - [x] Shutdowns
@@ -27,6 +40,32 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S21+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S21+ Hydro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21PlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## T21 (Stock) ## T21 (Stock)
- [x] Shutdowns - [x] Shutdowns
@@ -53,6 +92,58 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S21 Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 Pro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Pro
handler: python
options:
show_root_heading: false
heading_level: 0
## S21+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S21+ Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21PlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## T21 (BOS+) ## T21 (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -79,6 +170,71 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## S21 Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Pro
handler: python
options:
show_root_heading: false
heading_level: 0
## S21+ (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S21+ Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21PlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## T21 (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X21.T21.VNishT21
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 (ePIC) ## S21 (ePIC)
- [x] Shutdowns - [x] Shutdowns
@@ -122,7 +278,7 @@
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X21.S21.LUXMinerS21 ::: pyasic.miners.antminer.luxos.X21.S21.LUXMinerS21
@@ -131,6 +287,19 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## T21 (LuxOS)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X21.T21.LUXMinerT21
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 (MaraFW) ## S21 (MaraFW)
- [ ] Shutdowns - [ ] Shutdowns

View File

@@ -27,19 +27,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## L3+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## KA3 (Stock) ## KA3 (Stock)
- [x] Shutdowns - [x] Shutdowns
@@ -66,14 +53,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## L3+ (VNish) ## L3+ (Stock)
- [x] Shutdowns - [ ] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [x] Setpoints - [ ] Setpoints
- [x] Presets - [ ] Presets
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus ::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false

View File

@@ -27,3 +27,16 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## KS5 (Stock)
- [ ] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5Pro
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -1,14 +1,14 @@
# pyasic # pyasic
## X7 Models ## X7 Models
## L7 (Stock) ## D7 (Stock)
- [ ] Shutdowns - [x] Shutdowns
- [x] Power Modes - [x] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7 ::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -27,14 +27,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## D7 (Stock) ## L7 (Stock)
- [x] Shutdowns - [ ] Shutdowns
- [x] Power Modes - [x] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7 ::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false

View File

@@ -1,6 +1,19 @@
# pyasic # pyasic
## X9 Models ## X9 Models
## D9 (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
handler: python
options:
show_root_heading: false
heading_level: 0
## E9Pro (Stock) ## E9Pro (Stock)
- [ ] Shutdowns - [ ] Shutdowns
@@ -14,14 +27,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## D9 (Stock) ## L9 (Stock)
- [x] Shutdowns - [x] Shutdowns
- [x] Power Modes - [x] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9 ::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -79,19 +92,6 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## L9 (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
handler: python
options:
show_root_heading: false
heading_level: 0
## S9 (BOS+) ## S9 (BOS+)
- [x] Shutdowns - [x] Shutdowns
@@ -122,7 +122,7 @@
- [x] Shutdowns - [x] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [x] Setpoints
- [x] Presets - [x] Presets
::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9 ::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9

View File

@@ -0,0 +1,16 @@
# pyasic
## A15X Models
## Avalon 1566 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.avalonminer.cgminer.A15X.A1566.CGMinerAvalon1566
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -1,6 +1,19 @@
# pyasic # pyasic
## nano Models ## nano Models
## Avalon Nano 3s (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3s
handler: python
options:
show_root_heading: false
heading_level: 0
## Avalon Nano 3 (Stock) ## Avalon Nano 3 (Stock)
- [ ] Shutdowns - [ ] Shutdowns

View File

@@ -1,19 +1,6 @@
# pyasic # pyasic
## BM Models ## BM Models
## Supra (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
handler: python
options:
show_root_heading: false
heading_level: 0
## Ultra (Stock) ## Ultra (Stock)
- [ ] Shutdowns - [ ] Shutdowns
@@ -27,14 +14,14 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## Max (Stock) ## Supra (Stock)
- [ ] Shutdowns - [ ] Shutdowns
- [ ] Power Modes - [ ] Power Modes
- [ ] Setpoints - [ ] Setpoints
- [ ] Presets - [ ] Presets
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax ::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
@@ -53,3 +40,16 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## Max (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -27,3 +27,16 @@
show_root_heading: false show_root_heading: false
heading_level: 0 heading_level: 0
## BlockMiner eLITE 1.0 (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMinerELITE1
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,16 @@
# pyasic
## DGX Models
## DG1+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1Plus
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,29 @@
# pyasic
## LV Models
## LV07 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.luckyminer.espminer.LV.LV07.LuckyMinerLV07
handler: python
options:
show_root_heading: false
heading_level: 0
## LV08 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.luckyminer.espminer.LV.LV08.LuckyMinerLV08
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -32,6 +32,7 @@ details {
<ul> <ul>
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li> <li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li> <li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
@@ -100,8 +101,13 @@ 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-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#s21_1-stock">S21+ (Stock)</a></li>
<li><a href="../antminer/X21#s21_1-hydro-stock">S21+ Hydro (Stock)</a></li>
<li><a href="../antminer/X21#s21-pro-stock">S21 Pro (Stock)</a></li> <li><a href="../antminer/X21#s21-pro-stock">S21 Pro (Stock)</a></li>
<li><a href="../antminer/X21#t21-stock">T21 (Stock)</a></li> <li><a href="../antminer/X21#t21-stock">T21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-hydro-stock">S21 Hydro (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -548,6 +554,13 @@ details {
<summary>nano Series:</summary> <summary>nano Series:</summary>
<ul> <ul>
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li> <li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
<li><a href="../avalonminer/nano#avalon-nano-3s-stock">Avalon Nano 3s (Stock)</a></li>
</ul>
</details>
<details>
<summary>A15X Series:</summary>
<ul>
<li><a href="../avalonminer/A15X#avalon-1566-stock">Avalon 1566 (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -649,6 +662,10 @@ details {
<summary>X21 Series:</summary> <summary>X21 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li> <li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
<li><a href="../antminer/X21#s21-pro-bos_1">S21 Pro (BOS+)</a></li>
<li><a href="../antminer/X21#s21_1-bos_1">S21+ (BOS+)</a></li>
<li><a href="../antminer/X21#s21_1-hydro-bos_1">S21+ Hydro (BOS+)</a></li>
<li><a href="../antminer/X21#s21-hydro-bos_1">S21 Hydro (BOS+)</a></li>
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li> <li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
</ul> </ul>
</details> </details>
@@ -691,6 +708,8 @@ details {
<li><a href="../antminer/X19#s19-no-pic-vnish">S19 No PIC (VNish)</a></li> <li><a href="../antminer/X19#s19-no-pic-vnish">S19 No PIC (VNish)</a></li>
<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#s19i-vnish">S19i (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#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>
@@ -703,7 +722,12 @@ details {
<details> <details>
<summary>X21 Series:</summary> <summary>X21 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X21#t21-vnish">T21 (VNish)</a></li>
<li><a href="../antminer/X21#s21-vnish">S21 (VNish)</a></li> <li><a href="../antminer/X21#s21-vnish">S21 (VNish)</a></li>
<li><a href="../antminer/X21#s21_1-vnish">S21+ (VNish)</a></li>
<li><a href="../antminer/X21#s21_1-hydro-vnish">S21+ Hydro (VNish)</a></li>
<li><a href="../antminer/X21#s21-pro-vnish">S21 Pro (VNish)</a></li>
<li><a href="../antminer/X21#s21-hydro-vnish">S21 Hydro (VNish)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -722,6 +746,7 @@ details {
<li><a href="../antminer/X19#s19k-pro-epic">S19k Pro (ePIC)</a></li> <li><a href="../antminer/X19#s19k-pro-epic">S19k Pro (ePIC)</a></li>
<li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li> <li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li>
<li><a href="../antminer/X19#s19j-pro-dual-epic">S19j Pro Dual (ePIC)</a></li> <li><a href="../antminer/X19#s19j-pro-dual-epic">S19j Pro Dual (ePIC)</a></li>
<li><a href="../antminer/X19#s19k-pro-dual-epic">S19k Pro Dual (ePIC)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
@@ -737,6 +762,7 @@ details {
<ul> <ul>
<li><a href="../blockminer/blockminer#blockminer-520i-epic">BlockMiner 520i (ePIC)</a></li> <li><a href="../blockminer/blockminer#blockminer-520i-epic">BlockMiner 520i (ePIC)</a></li>
<li><a href="../blockminer/blockminer#blockminer-720i-epic">BlockMiner 720i (ePIC)</a></li> <li><a href="../blockminer/blockminer#blockminer-720i-epic">BlockMiner 720i (ePIC)</a></li>
<li><a href="../blockminer/blockminer#blockminer-elite-1.0-epic">BlockMiner eLITE 1.0 (ePIC)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -755,6 +781,19 @@ details {
<ul> <ul>
<li><a href="../antminer/X19#s19j-pro-hive">S19j Pro (Hive)</a></li> <li><a href="../antminer/X19#s19j-pro-hive">S19j Pro (Hive)</a></li>
<li><a href="../antminer/X19#s19-hive">S19 (Hive)</a></li> <li><a href="../antminer/X19#s19-hive">S19 (Hive)</a></li>
<li><a href="../antminer/X19#s19k-pro-hive">S19K Pro (Hive)</a></li>
<li><a href="../antminer/X19#s19-no-pic-hive">S19 No PIC (Hive)</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>MSKMiner Firmware Miners:</summary>
<ul>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="../antminer/X19#s19-no-pic-stock">S19 No PIC (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -784,6 +823,7 @@ details {
<summary>X21 Series:</summary> <summary>X21 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X21#s21-luxos">S21 (LuxOS)</a></li> <li><a href="../antminer/X21#s21-luxos">S21 (LuxOS)</a></li>
<li><a href="../antminer/X21#t21-luxos">T21 (LuxOS)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -854,6 +894,18 @@ details {
</ul> </ul>
</details> </details>
<details> <details>
<summary>Stock Firmware Lucky Miners:</summary>
<ul>
<details>
<summary>LV Series:</summary>
<ul>
<li><a href="../luckyminer/LV#lv08-stock">LV08 (Stock)</a></li>
<li><a href="../luckyminer/LV#lv07-stock">LV07 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware IceRiver Miners:</summary> <summary>Stock Firmware IceRiver Miners:</summary>
<ul> <ul>
<details> <details>
@@ -894,3 +946,14 @@ details {
</details> </details>
</ul> </ul>
</details> </details>
<details>
<summary>Stock Firmware Elphapex Miners:</summary>
<ul>
<details>
<summary>DGX Series:</summary>
<ul>
<li><a href="../elphapex/DGX#dg1_1-stock">DG1+ (Stock)</a></li>
</ul>
</details>
</ul>
</details>

857
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import importlib.metadata
from pyasic import settings from pyasic import settings
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import MinerData from pyasic.data import MinerData
@@ -22,3 +24,5 @@ from pyasic.network import MinerNetwork
from pyasic.rpc import * from pyasic.rpc import *
from pyasic.ssh import * from pyasic.ssh import *
from pyasic.web import * from pyasic.web import *
__version__ = importlib.metadata.version("pyasic")

View File

@@ -46,7 +46,7 @@ class MinerConfig(BaseModel):
"""Converts the MinerConfig object to a dictionary.""" """Converts the MinerConfig object to a dictionary."""
return self.model_dump() return self.model_dump()
def as_am_modern(self, user_suffix: str = None) -> dict: def as_am_modern(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for modern Antminers.""" """Generates the configuration in the format suitable for modern Antminers."""
return { return {
**self.fan_mode.as_am_modern(), **self.fan_mode.as_am_modern(),
@@ -56,7 +56,27 @@ class MinerConfig(BaseModel):
**self.temperature.as_am_modern(), **self.temperature.as_am_modern(),
} }
def as_wm(self, user_suffix: str = None) -> dict: def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for modern Hiveon."""
return {
**self.fan_mode.as_hiveon_modern(),
"freq-level": "100",
**self.mining_mode.as_hiveon_modern(),
**self.pools.as_hiveon_modern(user_suffix=user_suffix),
**self.temperature.as_hiveon_modern(),
}
def as_elphapex(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for modern Elphapex."""
return {
**self.fan_mode.as_elphapex(),
"fc-freq-level": "100",
**self.mining_mode.as_elphapex(),
**self.pools.as_elphapex(user_suffix=user_suffix),
**self.temperature.as_elphapex(),
}
def as_wm(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Whatsminers.""" """Generates the configuration in the format suitable for Whatsminers."""
return { return {
**self.fan_mode.as_wm(), **self.fan_mode.as_wm(),
@@ -65,7 +85,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_wm(), **self.temperature.as_wm(),
} }
def as_am_old(self, user_suffix: str = None) -> dict: def as_am_old(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for old versions of Antminers.""" """Generates the configuration in the format suitable for old versions of Antminers."""
return { return {
**self.fan_mode.as_am_old(), **self.fan_mode.as_am_old(),
@@ -74,7 +94,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_am_old(), **self.temperature.as_am_old(),
} }
def as_goldshell(self, user_suffix: str = None) -> dict: def as_goldshell(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Goldshell miners.""" """Generates the configuration in the format suitable for Goldshell miners."""
return { return {
**self.fan_mode.as_goldshell(), **self.fan_mode.as_goldshell(),
@@ -83,7 +103,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_goldshell(), **self.temperature.as_goldshell(),
} }
def as_avalon(self, user_suffix: str = None) -> dict: def as_avalon(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Avalonminers.""" """Generates the configuration in the format suitable for Avalonminers."""
return { return {
**self.fan_mode.as_avalon(), **self.fan_mode.as_avalon(),
@@ -92,7 +112,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_avalon(), **self.temperature.as_avalon(),
} }
def as_inno(self, user_suffix: str = None) -> dict: def as_inno(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Innosilicon miners.""" """Generates the configuration in the format suitable for Innosilicon miners."""
return { return {
**self.fan_mode.as_inno(), **self.fan_mode.as_inno(),
@@ -101,7 +121,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_inno(), **self.temperature.as_inno(),
} }
def as_bosminer(self, user_suffix: str = None) -> dict: def as_bosminer(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the bosminer.toml format.""" """Generates the configuration in the bosminer.toml format."""
return { return {
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()), **merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
@@ -109,7 +129,7 @@ class MinerConfig(BaseModel):
**self.pools.as_bosminer(user_suffix=user_suffix), **self.pools.as_bosminer(user_suffix=user_suffix),
} }
def as_boser(self, user_suffix: str = None) -> dict: def as_boser(self, user_suffix: str | None = 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(),
@@ -118,7 +138,7 @@ class MinerConfig(BaseModel):
**self.pools.as_boser(user_suffix=user_suffix), **self.pools.as_boser(user_suffix=user_suffix),
} }
def as_epic(self, user_suffix: str = None) -> dict: def as_epic(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for ePIC miners.""" """Generates the configuration in the format suitable for ePIC miners."""
return { return {
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()), **merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
@@ -126,7 +146,7 @@ class MinerConfig(BaseModel):
**self.pools.as_epic(user_suffix=user_suffix), **self.pools.as_epic(user_suffix=user_suffix),
} }
def as_auradine(self, user_suffix: str = None) -> dict: def as_auradine(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Auradine miners.""" """Generates the configuration in the format suitable for Auradine miners."""
return { return {
**self.fan_mode.as_auradine(), **self.fan_mode.as_auradine(),
@@ -135,7 +155,7 @@ class MinerConfig(BaseModel):
**self.pools.as_auradine(user_suffix=user_suffix), **self.pools.as_auradine(user_suffix=user_suffix),
} }
def as_mara(self, user_suffix: str = None) -> dict: def as_mara(self, user_suffix: str | None = None) -> dict:
return { return {
**self.fan_mode.as_mara(), **self.fan_mode.as_mara(),
**self.temperature.as_mara(), **self.temperature.as_mara(),
@@ -143,15 +163,15 @@ class MinerConfig(BaseModel):
**self.pools.as_mara(user_suffix=user_suffix), **self.pools.as_mara(user_suffix=user_suffix),
} }
def as_bitaxe(self, user_suffix: str = None) -> dict: def as_espminer(self, user_suffix: str | None = None) -> dict:
return { return {
**self.fan_mode.as_bitaxe(), **self.fan_mode.as_espminer(),
**self.temperature.as_bitaxe(), **self.temperature.as_espminer(),
**self.mining_mode.as_bitaxe(), **self.mining_mode.as_espminer(),
**self.pools.as_bitaxe(user_suffix=user_suffix), **self.pools.as_espminer(user_suffix=user_suffix),
} }
def as_luxos(self, user_suffix: str = None) -> dict: def as_luxos(self, user_suffix: str | None = None) -> dict:
return { return {
**self.fan_mode.as_luxos(), **self.fan_mode.as_luxos(),
**self.temperature.as_luxos(), **self.temperature.as_luxos(),
@@ -159,7 +179,7 @@ class MinerConfig(BaseModel):
**self.pools.as_luxos(user_suffix=user_suffix), **self.pools.as_luxos(user_suffix=user_suffix),
} }
def as_vnish(self, user_suffix: str = None) -> dict: def as_vnish(self, user_suffix: str | None = None) -> dict:
main_cfg = { main_cfg = {
"miner": { "miner": {
**self.fan_mode.as_vnish(), **self.fan_mode.as_vnish(),
@@ -199,6 +219,24 @@ class MinerConfig(BaseModel):
fan_mode=FanModeConfig.from_am_modern(web_conf), fan_mode=FanModeConfig.from_am_modern(web_conf),
) )
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for Hiveon."""
return cls(
pools=PoolConfig.from_hiveon_modern(web_conf),
mining_mode=MiningModeConfig.from_hiveon_modern(web_conf),
fan_mode=FanModeConfig.from_hiveon_modern(web_conf),
)
@classmethod
def from_elphapex(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for modern Antminers."""
return cls(
pools=PoolConfig.from_elphapex(web_conf),
mining_mode=MiningModeConfig.from_elphapex(web_conf),
fan_mode=FanModeConfig.from_elphapex(web_conf),
)
@classmethod @classmethod
def from_am_old(cls, web_conf: dict) -> "MinerConfig": def from_am_old(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for old versions of Antminers.""" """Constructs a MinerConfig object from web configuration for old versions of Antminers."""
@@ -245,13 +283,17 @@ class MinerConfig(BaseModel):
) )
@classmethod @classmethod
def from_vnish(cls, web_settings: dict, web_presets: list[dict]) -> "MinerConfig": def from_vnish(
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
) -> "MinerConfig":
"""Constructs a MinerConfig object from web settings for VNish miners.""" """Constructs a MinerConfig object from web settings for VNish miners."""
return cls( return cls(
pools=PoolConfig.from_vnish(web_settings), pools=PoolConfig.from_vnish(web_settings),
fan_mode=FanModeConfig.from_vnish(web_settings), fan_mode=FanModeConfig.from_vnish(web_settings),
temperature=TemperatureConfig.from_vnish(web_settings), temperature=TemperatureConfig.from_vnish(web_settings),
mining_mode=MiningModeConfig.from_vnish(web_settings, web_presets), mining_mode=MiningModeConfig.from_vnish(
web_settings, web_presets, web_perf_summary
),
) )
@classmethod @classmethod
@@ -272,10 +314,10 @@ class MinerConfig(BaseModel):
) )
@classmethod @classmethod
def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig": def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
return cls( return cls(
pools=PoolConfig.from_bitaxe(web_system_info), pools=PoolConfig.from_espminer(web_system_info),
fan_mode=FanModeConfig.from_bitaxe(web_system_info), fan_mode=FanModeConfig.from_espminer(web_system_info),
) )
@classmethod @classmethod
@@ -308,7 +350,3 @@ class MinerConfig(BaseModel):
@classmethod @classmethod
def from_hammer(cls, *args, **kwargs) -> "MinerConfig": def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
return cls.from_am_modern(*args, **kwargs) return cls.from_am_modern(*args, **kwargs)
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
return cls.from_am_modern(web_conf)

View File

@@ -28,6 +28,9 @@ class MinerConfigOption(Enum):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return self.value.as_am_modern() return self.value.as_am_modern()
def as_hiveon_modern(self) -> dict:
return self.value.as_hiveon_modern()
def as_am_old(self) -> dict: def as_am_old(self) -> dict:
return self.value.as_am_old() return self.value.as_am_old()
@@ -61,12 +64,15 @@ class MinerConfigOption(Enum):
def as_mara(self) -> dict: def as_mara(self) -> dict:
return self.value.as_mara() return self.value.as_mara()
def as_bitaxe(self) -> dict: def as_espminer(self) -> dict:
return self.value.as_bitaxe() return self.value.as_espminer()
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return self.value.as_luxos() return self.value.as_luxos()
def as_elphapex(self) -> dict:
return self.value.as_elphapex()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs) return self.value(*args, **kwargs)
@@ -92,6 +98,9 @@ class MinerConfigValue(BaseModel):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {} return {}
def as_hiveon_modern(self) -> dict:
return {}
def as_am_old(self) -> dict: def as_am_old(self) -> dict:
return {} return {}
@@ -125,12 +134,15 @@ class MinerConfigValue(BaseModel):
def as_mara(self) -> dict: def as_mara(self) -> dict:
return {} return {}
def as_bitaxe(self) -> dict: def as_espminer(self) -> dict:
return {} return {}
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {} return {}
def as_elphapex(self) -> dict:
return {}
def __getitem__(self, item): def __getitem__(self, item):
try: try:
return getattr(self, item) return getattr(self, item)

View File

@@ -55,6 +55,12 @@ class FanModeNormal(MinerConfigValue):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
def as_hiveon_modern(self) -> dict:
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
def as_elphapex(self) -> dict:
return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"}
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return { return {
"temp_control": {"mode": "auto"}, "temp_control": {"mode": "auto"},
@@ -81,7 +87,7 @@ class FanModeNormal(MinerConfigValue):
}, },
} }
def as_bitaxe(self) -> dict: def as_espminer(self) -> dict:
return {"autoFanspeed": 1} return {"autoFanspeed": 1}
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
@@ -135,6 +141,12 @@ class FanModeManual(MinerConfigValue):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)} return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
def as_hiveon_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
def as_elphapex(self) -> dict:
return {"fc-fan-ctrl": True, "fc-fan-pwm": str(self.speed)}
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return { return {
"temp_control": {"mode": "manual"}, "temp_control": {"mode": "manual"},
@@ -156,7 +168,7 @@ class FanModeManual(MinerConfigValue):
}, },
} }
def as_bitaxe(self) -> dict: def as_espminer(self) -> dict:
return {"autoFanspeed": 0, "fanspeed": self.speed} return {"autoFanspeed": 0, "fanspeed": self.speed}
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
@@ -185,6 +197,12 @@ class FanModeImmersion(MinerConfigValue):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"} return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
def as_hiveon_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
def as_elphapex(self) -> dict:
return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"}
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return { return {
"fan_control": {"min_fans": 0}, "fan_control": {"min_fans": 0},
@@ -239,6 +257,34 @@ class FanModeConfig(MinerConfigOption):
else: else:
return cls.default() return cls.default()
@classmethod
def from_hiveon_modern(cls, web_conf: dict):
if web_conf.get("bitmain-fan-ctrl") is not None:
fan_manual = web_conf["bitmain-fan-ctrl"]
if fan_manual:
speed = int(web_conf["bitmain-fan-pwm"])
if speed == 0:
return cls.immersion()
return cls.manual(speed=speed)
else:
return cls.normal()
else:
return cls.default()
@classmethod
def from_elphapex(cls, web_conf: dict):
if web_conf.get("fc-fan-ctrl") is not None:
fan_manual = web_conf["fc-fan-ctrl"]
if fan_manual:
speed = int(web_conf["fc-fan-pwm"])
if speed == 0:
return cls.immersion()
return cls.manual(speed=speed)
else:
return cls.normal()
else:
return cls.default()
@classmethod @classmethod
def from_epic(cls, web_conf: dict): def from_epic(cls, web_conf: dict):
try: try:
@@ -342,7 +388,7 @@ class FanModeConfig(MinerConfigOption):
return cls.default() return cls.default()
@classmethod @classmethod
def from_bitaxe(cls, web_system_info: dict): def from_espminer(cls, web_system_info: dict):
if web_system_info["autofanspeed"] == 1: if web_system_info["autofanspeed"] == 1:
return cls.normal() return cls.normal()
else: else:

View File

@@ -52,6 +52,14 @@ class MiningModeNormal(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict: def as_wm(self) -> dict:
return {"mode": self.mode} return {"mode": self.mode}
@@ -74,6 +82,9 @@ class MiningModeNormal(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": False}} return {"autotunerset": {"enabled": False}}
def as_bosminer(self) -> dict:
return {"autotuning": {"enabled": True}}
class MiningModeSleep(MinerConfigValue): class MiningModeSleep(MinerConfigValue):
mode: str = field(init=False, default="sleep") mode: str = field(init=False, default="sleep")
@@ -87,6 +98,14 @@ class MiningModeSleep(MinerConfigValue):
return {"miner-mode": "1"} return {"miner-mode": "1"}
return {"miner-mode": 1} return {"miner-mode": 1}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "1"}
return {"miner-mode": 1}
def as_elphapex(self) -> dict:
return {"miner-mode": 1}
def as_wm(self) -> dict: def as_wm(self) -> dict:
return {"mode": self.mode} return {"mode": self.mode}
@@ -119,6 +138,14 @@ class MiningModeLPM(MinerConfigValue):
return {"miner-mode": "3"} return {"miner-mode": "3"}
return {"miner-mode": 3} return {"miner-mode": 3}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "3"}
return {"miner-mode": 3}
def as_elphapex(self) -> dict:
return {"miner-mode": 3}
def as_wm(self) -> dict: def as_wm(self) -> dict:
return {"mode": self.mode} return {"mode": self.mode}
@@ -141,6 +168,14 @@ class MiningModeHPM(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict: def as_wm(self) -> dict:
return {"mode": self.mode} return {"mode": self.mode}
@@ -174,6 +209,14 @@ class MiningModePowerTune(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict: def as_wm(self) -> dict:
if self.power is not None: if self.power is not None:
return {"mode": self.mode, self.mode: {"wattage": self.power}} return {"mode": self.mode, self.mode: {"wattage": self.power}}
@@ -215,18 +258,18 @@ class MiningModePowerTune(MinerConfigValue):
sd_cfg = {} sd_cfg = {}
if self.scaling.shutdown is not None: if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser() sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = ( power_target_kwargs = {}
SetDpsRequest( if self.scaling.step is not None:
power_target_kwargs["power_step"] = Power(self.scaling.step)
if self.scaling.minimum is not None:
power_target_kwargs["min_power_target"] = Power(self.scaling.minimum)
cfg["set_dps"] = SetDpsRequest(
save_action=SaveAction.SAVE_AND_APPLY,
enable=True, enable=True,
**sd_cfg, **sd_cfg,
target=DpsTarget( target=DpsTarget(power_target=DpsPowerTarget(**power_target_kwargs)),
power_target=DpsPowerTarget(
power_step=Power(self.scaling.step),
min_power_target=Power(self.scaling.minimum),
)
),
),
) )
return cfg return cfg
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
@@ -273,13 +316,20 @@ class MiningModeHashrateTune(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
conf = {"enabled": True, "mode": "hashrate_target"} conf = {"enabled": True, "mode": "hashrate_target"}
if self.hashrate is not None: if self.hashrate is not None:
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 = { cfg = {
"set_performance_mode": SetPerformanceModeRequest( "set_performance_mode": SetPerformanceModeRequest(
@@ -299,18 +349,24 @@ class MiningModeHashrateTune(MinerConfigValue):
sd_cfg = {} sd_cfg = {}
if self.scaling.shutdown is not None: if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser() sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = ( hashrate_target_kwargs = {}
SetDpsRequest( if self.scaling.step is not None:
hashrate_target_kwargs["hashrate_step"] = TeraHashrate(
self.scaling.step
)
if self.scaling.minimum is not None:
hashrate_target_kwargs["min_hashrate_target"] = TeraHashrate(
self.scaling.minimum
)
cfg["set_dps"] = SetDpsRequest(
save_action=SaveAction.SAVE_AND_APPLY,
enable=True, enable=True,
**sd_cfg, **sd_cfg,
target=DpsTarget( target=DpsTarget(
hashrate_target=DpsHashrateTarget( hashrate_target=DpsHashrateTarget(**hashrate_target_kwargs)
hashrate_step=TeraHashrate(self.scaling.step),
min_hashrate_target=TeraHashrate(self.scaling.minimum),
)
),
), ),
) )
return cfg return cfg
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
@@ -356,32 +412,43 @@ class MiningModePreset(MinerConfigValue):
@classmethod @classmethod
def from_vnish( def from_vnish(
cls, web_overclock_settings: dict, web_presets: list[dict] cls,
web_overclock_settings: dict,
web_presets: list[dict],
web_perf_summary: dict,
) -> "MiningModePreset": ) -> "MiningModePreset":
active_preset = None active_preset = web_perf_summary.get("current_preset")
if active_preset is None:
for preset in web_presets: for preset in web_presets:
if preset["name"] == web_overclock_settings["preset"]: if preset["name"] == web_overclock_settings["preset"]:
active_preset = preset active_preset = preset
return cls( return cls(
active_preset=MiningPreset.from_vnish(active_preset), active_preset=MiningPreset.from_vnish(active_preset),
available_presets=[MiningPreset.from_vnish(p) for p in web_presets], available_presets=[MiningPreset.from_vnish(p) for p in web_presets],
) )
@classmethod @classmethod
def from_luxos( def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> "MiningModePreset":
cls, rpc_config: dict, rpc_profiles: list[dict] active_preset = cls.get_active_preset_from_luxos(rpc_config, rpc_profiles)
) -> "MiningModePreset": return cls(
active_preset=active_preset,
available_presets=[
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
],
)
@classmethod
def get_active_preset_from_luxos(
cls, rpc_config: dict, rpc_profiles: dict
) -> MiningPreset:
active_preset = None active_preset = None
active_profile = rpc_config["CONFIG"][0]["Profile"] active_profile = rpc_config["CONFIG"][0]["Profile"]
for profile in rpc_profiles["PROFILES"]: for profile in rpc_profiles["PROFILES"]:
if profile["Profile Name"] == active_profile: if profile["Profile Name"] == active_profile:
active_preset = profile active_preset = profile
return cls( return MiningPreset.from_luxos(active_preset)
active_preset=MiningPreset.from_luxos(active_preset),
available_presets=[
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
],
)
class ManualBoardSettings(MinerConfigValue): class ManualBoardSettings(MinerConfigValue):
@@ -397,6 +464,14 @@ class ManualBoardSettings(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_vnish(self) -> dict: def as_vnish(self) -> dict:
return {"freq": self.freq} return {"freq": self.freq}
@@ -421,6 +496,9 @@ class MiningModeManual(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_vnish(self) -> dict: def as_vnish(self) -> dict:
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0] chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
return { return {
@@ -518,6 +596,34 @@ class MiningModeConfig(MinerConfigOption):
return cls.low() return cls.low()
return cls.default() return cls.default()
@classmethod
def from_hiveon_modern(cls, web_conf: dict):
if web_conf.get("bitmain-work-mode") is not None:
work_mode = web_conf["bitmain-work-mode"]
if work_mode == "":
return cls.default()
if int(work_mode) == 0:
return cls.normal()
elif int(work_mode) == 1:
return cls.sleep()
elif int(work_mode) == 3:
return cls.low()
return cls.default()
@classmethod
def from_elphapex(cls, web_conf: dict):
if web_conf.get("fc-work-mode") is not None:
work_mode = web_conf["fc-work-mode"]
if work_mode == "":
return cls.default()
if int(work_mode) == 0:
return cls.normal()
elif int(work_mode) == 1:
return cls.sleep()
elif int(work_mode) == 3:
return cls.low()
return cls.default()
@classmethod @classmethod
def from_epic(cls, web_conf: dict): def from_epic(cls, web_conf: dict):
try: try:
@@ -600,9 +706,12 @@ class MiningModeConfig(MinerConfigOption):
return cls.hashrate_tuning( return cls.hashrate_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"), scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
) )
return cls.default()
@classmethod @classmethod
def from_vnish(cls, web_settings: dict, web_presets: list[dict]): def from_vnish(
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
):
try: try:
mode_settings = web_settings["miner"]["overclock"] mode_settings = web_settings["miner"]["overclock"]
except KeyError: except KeyError:
@@ -611,7 +720,9 @@ 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 MiningModePreset.from_vnish(mode_settings, web_presets) return MiningModePreset.from_vnish(
mode_settings, web_presets, web_perf_summary
)
@classmethod @classmethod
def from_boser(cls, grpc_miner_conf: dict): def from_boser(cls, grpc_miner_conf: dict):
@@ -705,7 +816,11 @@ class MiningModeConfig(MinerConfigOption):
@classmethod @classmethod
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict): def from_luxos(cls, rpc_config: dict, rpc_profiles: dict):
return MiningModePreset.from_luxos(rpc_config, rpc_profiles) preset_info = MiningModePreset.from_luxos(rpc_config, rpc_profiles)
return cls.preset(
active_preset=preset_info.active_preset,
available_presets=preset_info.available_presets,
)
MiningMode = TypeVar( MiningMode = TypeVar(

View File

@@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import field
from typing import TypeVar, Union from typing import TypeVar, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue from pyasic.config.base import MinerConfigOption, MinerConfigValue

View File

@@ -24,7 +24,14 @@ class MiningPreset(MinerConfigValue):
hashrate = None hashrate = None
else: else:
power = hr_power_split[0].replace("watt", "").strip() power = hr_power_split[0].replace("watt", "").strip()
hashrate = hr_power_split[1].replace("TH", "").replace(" LC", "").strip() hashrate = (
hr_power_split[1]
.replace("TH", "")
.replace("GH", "")
.replace("MH", "")
.replace(" LC", "")
.strip()
)
tuned = web_preset["status"] == "tuned" tuned = web_preset["status"] == "tuned"
modded_psu = web_preset["modded_psu_required"] modded_psu = web_preset["modded_psu_required"]
return cls( return cls(

View File

@@ -15,14 +15,12 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue from pyasic.config.base import MinerConfigValue
class ScalingShutdown(MinerConfigValue): class ScalingShutdown(MinerConfigValue):
enabled: bool = False enabled: bool = False
duration: int = None duration: int | None = None
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown": def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown":
@@ -65,9 +63,9 @@ class ScalingShutdown(MinerConfigValue):
class ScalingConfig(MinerConfigValue): class ScalingConfig(MinerConfigValue):
step: int = None step: int | None = None
minimum: int = None minimum: int | None = None
shutdown: ScalingShutdown = None shutdown: ScalingShutdown | None = None
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig": def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig":
@@ -81,7 +79,7 @@ class ScalingConfig(MinerConfigValue):
return cls(**cls_conf) return cls(**cls_conf)
@classmethod @classmethod
def from_bosminer(cls, toml_conf: dict, mode: str = None): def from_bosminer(cls, toml_conf: dict, mode: str | None = None):
if mode == "power": if mode == "power":
return cls._from_bosminer_power(toml_conf) return cls._from_bosminer_power(toml_conf)
if mode == "hashrate": if mode == "hashrate":
@@ -106,7 +104,7 @@ class ScalingConfig(MinerConfigValue):
return cls(step=power_step, minimum=min_power, shutdown=sd_mode) return cls(step=power_step, minimum=min_power, shutdown=sd_mode)
@classmethod @classmethod
def from_boser(cls, grpc_miner_conf: dict, mode: str = None): def from_boser(cls, grpc_miner_conf: dict, mode: str | None = None):
if mode == "power": if mode == "power":
return cls._from_boser_power(grpc_miner_conf) return cls._from_boser_power(grpc_miner_conf)
if mode == "hashrate": if mode == "hashrate":

View File

@@ -43,6 +43,20 @@ class Pool(MinerConfigValue):
"pass": self.password, "pass": self.password,
} }
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
return {
"url": self.url,
"user": f"{self.user}{user_suffix or ''}",
"pass": self.password,
}
def as_elphapex(self, user_suffix: str | None = None) -> dict:
return {
"url": self.url,
"user": f"{self.user}{user_suffix or ''}",
"pass": self.password,
}
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict: def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
return { return {
f"pool_{idx}": self.url, f"pool_{idx}": self.url,
@@ -102,7 +116,7 @@ class Pool(MinerConfigValue):
"pass": self.password, "pass": self.password,
} }
def as_bitaxe(self, user_suffix: str | None = None) -> dict: def as_espminer(self, user_suffix: str | None = None) -> dict:
return { return {
"stratumURL": self.url, "stratumURL": self.url,
"stratumUser": f"{self.user}{user_suffix or ''}", "stratumUser": f"{self.user}{user_suffix or ''}",
@@ -146,6 +160,18 @@ class Pool(MinerConfigValue):
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
) )
@classmethod
def from_hiveon_modern(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
@classmethod
def from_elphapex(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
# TODO: check if this is accurate, user/username, pass/password # TODO: check if this is accurate, user/username, pass/password
@classmethod @classmethod
def from_goldshell(cls, web_pool: dict) -> "Pool": def from_goldshell(cls, web_pool: dict) -> "Pool":
@@ -164,7 +190,7 @@ class Pool(MinerConfigValue):
return cls( return cls(
url=toml_pool_conf["url"], url=toml_pool_conf["url"],
user=toml_pool_conf["user"], user=toml_pool_conf["user"],
password=toml_pool_conf["password"], password=toml_pool_conf.get("password", ""),
) )
@classmethod @classmethod
@@ -192,7 +218,7 @@ class Pool(MinerConfigValue):
) )
@classmethod @classmethod
def from_bitaxe(cls, web_system_info: dict) -> "Pool": def from_espminer(cls, web_system_info: dict) -> "Pool":
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}" url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
return cls( return cls(
url=url, url=url,
@@ -235,6 +261,28 @@ class PoolGroup(MinerConfigValue):
idx += 1 idx += 1
return pools return pools
def as_hiveon_modern(self, user_suffix: str | None = None) -> list:
pools = []
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.append(self.pools[idx].as_hiveon_modern(user_suffix=user_suffix))
else:
pools.append(Pool(url="", user="", password="").as_hiveon_modern())
idx += 1
return pools
def as_elphapex(self, user_suffix: str | None = None) -> list:
pools = []
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.append(self.pools[idx].as_elphapex(user_suffix=user_suffix))
else:
pools.append(Pool(url="", user="", password="").as_elphapex())
idx += 1
return pools
def as_wm(self, user_suffix: str | None = None) -> dict: def as_wm(self, user_suffix: str | None = None) -> dict:
pools = {} pools = {}
idx = 0 idx = 0
@@ -306,8 +354,8 @@ class PoolGroup(MinerConfigValue):
def as_mara(self, user_suffix: str | None = None) -> list: def as_mara(self, user_suffix: str | None = None) -> list:
return [p.as_mara(user_suffix=user_suffix) for p in self.pools] return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
def as_bitaxe(self, user_suffix: str | None = None) -> dict: def as_espminer(self, user_suffix: str | None = None) -> dict:
return self.pools[0].as_bitaxe(user_suffix=user_suffix) return self.pools[0].as_espminer(user_suffix=user_suffix)
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration: def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
return PoolGroupConfiguration( return PoolGroupConfiguration(
@@ -351,6 +399,20 @@ class PoolGroup(MinerConfigValue):
pools.append(Pool.from_am_modern(pool)) pools.append(Pool.from_am_modern(pool))
return cls(pools=pools) return cls(pools=pools)
@classmethod
def from_hiveon_modern(cls, web_pool_list: list) -> "PoolGroup":
pools = []
for pool in web_pool_list:
pools.append(Pool.from_hiveon_modern(pool))
return cls(pools=pools)
@classmethod
def from_elphapex(cls, web_pool_list: list) -> "PoolGroup":
pools = []
for pool in web_pool_list:
pools.append(Pool.from_elphapex(pool))
return cls(pools=pools)
@classmethod @classmethod
def from_goldshell(cls, web_pools: list) -> "PoolGroup": def from_goldshell(cls, web_pools: list) -> "PoolGroup":
return cls(pools=[Pool.from_goldshell(p) for p in web_pools]) return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
@@ -395,8 +457,8 @@ class PoolGroup(MinerConfigValue):
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools]) return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
@classmethod @classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup": def from_espminer(cls, web_system_info: dict) -> "PoolGroup":
return cls(pools=[Pool.from_bitaxe(web_system_info)]) return cls(pools=[Pool.from_espminer(web_system_info)])
@classmethod @classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup": def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup":
@@ -436,6 +498,16 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)} return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_am_modern()} return {"pools": PoolGroup().as_am_modern()}
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_hiveon_modern(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_hiveon_modern()}
def as_elphapex(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_elphapex(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_elphapex()}
def as_wm(self, user_suffix: str | None = None) -> dict: def as_wm(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0: if len(self.groups) > 0:
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)} return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
@@ -507,8 +579,8 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)} return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
return {"pools": []} return {"pools": []}
def as_bitaxe(self, user_suffix: str | None = None) -> dict: def as_espminer(self, user_suffix: str | None = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix) return self.groups[0].as_espminer(user_suffix=user_suffix)
def as_luxos(self, user_suffix: str | None = None) -> dict: def as_luxos(self, user_suffix: str | None = None) -> dict:
return {} return {}
@@ -533,10 +605,28 @@ class PoolConfig(MinerConfigValue):
@classmethod @classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig": def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
try:
pool_data = web_conf["pools"] pool_data = web_conf["pools"]
except KeyError:
return cls(groups=[])
return cls(groups=[PoolGroup.from_am_modern(pool_data)]) return cls(groups=[PoolGroup.from_am_modern(pool_data)])
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "PoolConfig":
try:
pool_data = web_conf["pools"]
except KeyError:
return cls(groups=[])
return cls(groups=[PoolGroup.from_hiveon_modern(pool_data)])
@classmethod
def from_elphapex(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"]
return cls(groups=[PoolGroup.from_elphapex(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(groups=[PoolGroup.from_goldshell(web_pools)])
@@ -576,8 +666,8 @@ class PoolConfig(MinerConfigValue):
return cls(groups=[PoolGroup.from_mara(web_config["pools"])]) return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
@classmethod @classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig": def from_espminer(cls, web_system_info: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_bitaxe(web_system_info)]) return cls(groups=[PoolGroup.from_espminer(web_system_info)])
@classmethod @classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig": def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":

View File

@@ -15,8 +15,6 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue from pyasic.config.base import MinerConfigValue

View File

@@ -16,7 +16,7 @@
import copy import copy
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any, List, Union from typing import Any
from pydantic import BaseModel, Field, computed_field from pydantic import BaseModel, Field, computed_field
@@ -24,12 +24,11 @@ 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.data.pools import PoolMetrics, Scheme
from pyasic.device.algorithm.hashrate import AlgoHashRateType from pyasic.device.algorithm.hashrate import AlgoHashRateType
from pyasic.device.algorithm.hashrate.base import GenericHashrate
from ..device.algorithm.hashrate.unit.base import GenericUnit
from .boards import HashBoard from .boards import HashBoard
from .device import DeviceInfo from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .error_codes.base import BaseMinerError
from .fans import Fan from .fans import Fan
@@ -71,6 +70,7 @@ class MinerData(BaseModel):
errors: A list of errors on the miner. errors: A list of errors on the miner.
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.
efficiency_fract: Same as efficiency, but is not rounded to integer. 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. pools: A list of PoolMetrics instances, each representing metrics for a pool.
""" """
@@ -109,25 +109,18 @@ class MinerData(BaseModel):
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False) raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False)
# fans # fans
fans: List[Fan] = Field(default_factory=list) fans: list[Fan] = Field(default_factory=list)
fan_psu: int | None = None fan_psu: int | None = None
# boards # boards
hashboards: List[HashBoard] = Field(default_factory=list) hashboards: list[HashBoard] = Field(default_factory=list)
# config # config
config: MinerConfig | None = None config: MinerConfig | None = None
fault_light: bool | None = None fault_light: bool | None = None
# errors # errors
errors: List[ errors: list[BaseMinerError] = Field(default_factory=list)
Union[
WhatsminerError,
BraiinsOSError,
X19Error,
InnosiliconError,
]
] = Field(default_factory=list)
# mining state # mining state
is_mining: bool = True is_mining: bool = True
@@ -137,8 +130,10 @@ class MinerData(BaseModel):
pools: list[PoolMetrics] = Field(default_factory=list) pools: list[PoolMetrics] = Field(default_factory=list)
@classmethod @classmethod
def fields(cls): def fields(cls) -> set:
return list(cls.model_fields.keys()) all_fields = set(cls.model_fields.keys())
all_fields.update(set(cls.model_computed_fields.keys()))
return all_fields
def get(self, __key: str, default: Any = None): def get(self, __key: str, default: Any = None):
try: try:
@@ -290,12 +285,24 @@ class MinerData(BaseModel):
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property
def efficiency(self) -> int | None: def efficiency(self) -> int | None:
efficiency = self._efficiency(0)
if efficiency is None:
return None
else:
return int(efficiency)
@computed_field # type: ignore[misc]
@property
def efficiency_fract(self) -> float | None:
return self._efficiency(2)
def _efficiency(self, ndigits: int) -> float | None:
if self.hashrate is None or self.wattage is None: if self.hashrate is None or self.wattage is None:
return None return None
try: try:
return round(self.wattage / float(self.hashrate)) return round(self.wattage / float(self.hashrate), ndigits)
except ZeroDivisionError: except ZeroDivisionError:
return 0 return 0.0
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property
@@ -309,25 +316,25 @@ class MinerData(BaseModel):
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property
def make(self) -> str: def make(self) -> str | None:
if self.device_info.make is not None: if self.device_info.make is not None:
return str(self.device_info.make) return str(self.device_info.make)
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property
def model(self) -> str: def model(self) -> str | None:
if self.device_info.model is not None: if self.device_info.model is not None:
return str(self.device_info.model) return str(self.device_info.model)
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property
def firmware(self) -> str: def firmware(self) -> str | None:
if self.device_info.firmware is not None: if self.device_info.firmware is not None:
return str(self.device_info.firmware) return str(self.device_info.firmware)
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property
def algo(self) -> str: def algo(self) -> str | None:
if self.device_info.algo is not None: if self.device_info.algo is not None:
return str(self.device_info.algo) return str(self.device_info.algo)
@@ -367,7 +374,9 @@ class MinerData(BaseModel):
data_list = [str(data[item]) for item in data] data_list = [str(data[item]) for item in data]
return ",".join(data_list) return ",".join(data_list)
def as_influxdb(self, measurement_name: str = "miner_data") -> str: def as_influxdb(
self, measurement_name: str = "miner_data", level_delimiter: str = "."
) -> str:
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/). """Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
Parameters: Parameters:
@@ -376,54 +385,127 @@ class MinerData(BaseModel):
Returns: Returns:
A influxdb line protocol version of this class. A influxdb line protocol version of this class.
""" """
tag_data = [measurement_name]
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
return f"{key}={round(float(value), 2)}"
def serialize_list(key: str, value: list[Any]) -> str | None:
if len(value) == 0:
return None
list_field_data = []
for idx, list_field_val in enumerate(value):
item_serialization_func = serialization_map.get(
type(list_field_val), lambda _k, _v: None
)
item_serialized = item_serialization_func(
f"{key}{level_delimiter}{idx}", list_field_val
)
if item_serialized is not None:
list_field_data.append(item_serialized)
continue
for dt in serialization_map_instance:
if item_serialized is None:
if isinstance(list_field_val, dt):
item_serialized = serialization_map_instance[dt](
f"{key}{level_delimiter}{idx}", list_field_val
)
if item_serialized is not None:
list_field_data.append(item_serialized)
return ",".join(list_field_data)
def serialize_miner_error(key: str, value: BaseMinerError):
return value.as_influxdb(key, level_delimiter=level_delimiter)
def serialize_fan(key: str, value: Fan) -> str:
return f"{key}{level_delimiter}speed={value.speed}"
def serialize_hashboard(key: str, value: HashBoard) -> str:
return value.as_influxdb(key, level_delimiter=level_delimiter)
def serialize_bool(key: str, value: bool):
return f"{key}={str(value).lower()}"
def serialize_pool_metrics(key: str, value: PoolMetrics):
return value.as_influxdb(key, level_delimiter=level_delimiter)
include = [
"uptime",
"expected_hashrate",
"hashrate",
"hashboards",
"temperature_avg",
"env_temp",
"wattage",
"wattage_limit",
"voltage",
"fans",
"expected_fans",
"fan_psu",
"total_chips",
"expected_chips",
"efficiency",
"fault_light",
"is_mining",
"errors",
"pools",
]
serialization_map_instance = {
AlgoHashRateType: serialize_algo_hash_rate,
BaseMinerError: serialize_miner_error,
}
serialization_map = {
int: serialize_int,
float: serialize_float,
str: serialize_str,
bool: serialize_bool,
list: serialize_list,
Fan: serialize_fan,
HashBoard: serialize_hashboard,
PoolMetrics: serialize_pool_metrics,
}
tag_data = [
measurement_name,
f"ip={str(self.ip)}",
f"mac={str(self.mac)}",
f"make={str(self.make)}",
f"model={str(self.model)}",
f"firmware={str(self.firmware)}",
f"algo={str(self.algo)}",
]
field_data = [] field_data = []
tags = ["ip", "mac", "model", "hostname"] for field in include:
for attribute in self.fields(): field_val = getattr(self, field)
if attribute in tags: serialization_func = serialization_map.get(
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ") type(field_val), lambda _k, _v: None
tag_data.append(f"{attribute}={escaped_data}")
continue
elif str(attribute).startswith("_"):
continue
elif isinstance(self[attribute], str):
field_data.append(f'{attribute}="{self[attribute]}"')
continue
elif isinstance(self[attribute], bool):
field_data.append(f"{attribute}={str(self[attribute]).lower()}")
continue
elif isinstance(self[attribute], int):
field_data.append(f"{attribute}={self[attribute]}")
continue
elif isinstance(self[attribute], float):
field_data.append(f"{attribute}={self[attribute]}")
continue
elif attribute == "errors":
for idx, item in enumerate(self[attribute]):
field_data.append(f'error_{idx+1}="{item.error_message}"')
elif attribute == "hashboards":
for idx, item in enumerate(self[attribute]):
field_data.append(
f"hashboard_{idx+1}_hashrate={item.get('hashrate', 0.0)}"
) )
field_data.append( serialized = serialization_func(field, field_val)
f"hashboard_{idx+1}_temperature={item.get('temp', 0)}" if serialized is not None:
field_data.append(serialized)
continue
for datatype in serialization_map_instance:
if serialized is None:
if isinstance(field_val, datatype):
serialized = serialization_map_instance[datatype](
field, field_val
) )
field_data.append( if serialized is not None:
f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}" field_data.append(serialized)
)
field_data.append(f"hashboard_{idx+1}_chips={item.get('chips', 0)}")
field_data.append(
f"hashboard_{idx+1}_expected_chips={item.get('expected_chips', 0)}"
)
elif attribute == "fans":
for idx, item in enumerate(self[attribute]):
if item.speed is not None:
field_data.append(f"fan_{idx+1}={item.speed}")
tags_str = ",".join(tag_data) tags_str = ",".join(tag_data).replace(" ", "\\ ")
field_str = ",".join(field_data) field_str = ",".join(field_data).replace(" ", "\\ ")
timestamp = str(self.timestamp * 1e9) timestamp = str(self.timestamp * 10**9)
return " ".join([tags_str, field_str, timestamp]) return " ".join([tags_str, field_str, timestamp])

View File

@@ -17,7 +17,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from pydantic import BaseModel, field_serializer from pydantic import BaseModel
from pyasic.device.algorithm.hashrate import AlgoHashRateType from pyasic.device.algorithm.hashrate import AlgoHashRateType
@@ -28,6 +28,8 @@ class HashBoard(BaseModel):
Attributes: Attributes:
slot: The slot of the board as an int. slot: The slot of the board as an int.
hashrate: The hashrate of the board in TH/s as a float. hashrate: The hashrate of the board in TH/s as a float.
inlet_temp: Inlet temperature for hydro asics as an int
outlet_temp: Outlet temperature for hydro asics as an int
temp: The temperature of the PCB as an int. temp: The temperature of the PCB as an int.
chip_temp: The temperature of the chips as an int. chip_temp: The temperature of the chips as an int.
chips: The chip count of the board as an int. chips: The chip count of the board as an int.
@@ -41,6 +43,8 @@ class HashBoard(BaseModel):
slot: int = 0 slot: int = 0
hashrate: AlgoHashRateType | None = None hashrate: AlgoHashRateType | None = None
inlet_temp: float | None = None
outlet_temp: float | None = None
temp: float | None = None temp: float | None = None
chip_temp: float | None = None chip_temp: float | None = None
chips: int | None = None chips: int | None = None
@@ -51,6 +55,12 @@ class HashBoard(BaseModel):
active: bool | None = None active: bool | None = None
voltage: float | None = None voltage: float | None = None
@classmethod
def fields(cls) -> set:
all_fields = set(cls.model_fields.keys())
all_fields.update(set(cls.model_computed_fields.keys()))
return all_fields
def get(self, __key: str, default: Any = None): def get(self, __key: str, default: Any = None):
try: try:
val = self.__getitem__(__key) val = self.__getitem__(__key)
@@ -65,3 +75,63 @@ class HashBoard(BaseModel):
return getattr(self, item) return getattr(self, item)
except AttributeError: except AttributeError:
raise KeyError(f"{item}") raise KeyError(f"{item}")
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
return f"{key}={round(float(value), 2)}"
def serialize_bool(key: str, value: bool):
return f"{key}={str(value).lower()}"
serialization_map_instance = {
AlgoHashRateType: serialize_algo_hash_rate,
}
serialization_map = {
int: serialize_int,
float: serialize_float,
str: serialize_str,
bool: serialize_bool,
}
include = [
"hashrate",
"temp",
"chip_temp",
"chips",
"expected_chips",
"tuned",
"active",
"voltage",
]
field_data = []
for field in include:
field_val = getattr(self, field)
serialization_func = serialization_map.get(
type(field_val), lambda _k, _v: None
)
serialized = serialization_func(
f"{key_root}{level_delimiter}{field}", field_val
)
if serialized is not None:
field_data.append(serialized)
continue
for datatype in serialization_map_instance:
if serialized is None:
if isinstance(field_val, datatype):
serialized = serialization_map_instance[datatype](
f"{key_root}{level_delimiter}{field}", field_val
)
if serialized is not None:
field_data.append(serialized)
return ",".join(field_data)

View File

@@ -18,9 +18,15 @@ from typing import TypeVar
from .bos import BraiinsOSError from .bos import BraiinsOSError
from .innosilicon import InnosiliconError from .innosilicon import InnosiliconError
from .vnish import VnishError
from .whatsminer import WhatsminerError from .whatsminer import WhatsminerError
from .X19 import X19Error from .X19 import X19Error
MinerErrorData = TypeVar( MinerErrorData = TypeVar(
"MinerErrorData", WhatsminerError, BraiinsOSError, X19Error, InnosiliconError "MinerErrorData",
WhatsminerError,
BraiinsOSError,
X19Error,
InnosiliconError,
VnishError,
) )

View File

@@ -16,3 +16,17 @@ class BaseMinerError(BaseModel):
A dictionary version of this class. A dictionary version of this class.
""" """
return self.asdict() return self.asdict()
def as_influxdb(self, root_key: str, level_delimiter: str = ".") -> str:
field_data = []
if self.error_code is not None:
field_data.append(
f"{root_key}{level_delimiter}error_code={self.error_code}"
)
if self.error_message is not None:
field_data.append(
f'{root_key}{level_delimiter}error_message="{self.error_message}"'
)
return ",".join(field_data)

View File

@@ -0,0 +1,28 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.data.error_codes.base import BaseMinerError
class VnishError(BaseMinerError):
"""A Dataclass to handle error codes of Vnish miners.
Attributes:
error_message: The error message as a string.
error_code: The error code as an int. 0 if the message is not assigned a code.
"""
error_message: str
error_code: int = 0

View File

@@ -92,3 +92,51 @@ class PoolMetrics(BaseModel):
if total == 0: if total == 0:
return 0 return 0
return (value / total) * 100 return (value / total) * 100
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_pool_url(key: str, value: str) -> str:
return f'{key}="{str(value)}"'
def serialize_bool(key: str, value: bool):
return f"{key}={str(value).lower()}"
serialization_map = {
int: serialize_int,
float: serialize_float,
str: serialize_str,
bool: serialize_bool,
PoolUrl: serialize_pool_url,
}
include = [
"url",
"accepted",
"rejected",
"active",
"alive",
"user",
]
field_data = []
for field in include:
field_val = getattr(self, field)
serialization_func = serialization_map.get(
type(field_val), lambda _k, _v: None
)
serialized = serialization_func(
f"{key_root}{level_delimiter}{field}", field_val
)
if serialized is not None:
field_data.append(serialized)
return ",".join(field_data)

View File

@@ -26,9 +26,11 @@ class MinerMake(str, Enum):
AURADINE = "Auradine" AURADINE = "Auradine"
EPIC = "ePIC" EPIC = "ePIC"
BITAXE = "BitAxe" BITAXE = "BitAxe"
LUCKYMINER = "LuckyMiner"
ICERIVER = "IceRiver" ICERIVER = "IceRiver"
HAMMER = "Hammer" HAMMER = "Hammer"
VOLCMINER = "VolcMiner" VOLCMINER = "VolcMiner"
ELPHAPEX = "Elphapex"
BRAIINS = "Braiins" BRAIINS = "Braiins"
def __str__(self): def __str__(self):

View File

@@ -13,6 +13,7 @@ class AntminerModels(MinerModelType):
KS3 = "KS3" KS3 = "KS3"
DR5 = "DR5" DR5 = "DR5"
KS5 = "KS5" KS5 = "KS5"
KS5Pro = "KS5Pro"
L7 = "L7" L7 = "L7"
K7 = "K7" K7 = "K7"
D7 = "D7" D7 = "D7"
@@ -57,7 +58,10 @@ class AntminerModels(MinerModelType):
S19jXP = "S19j XP" S19jXP = "S19j XP"
T19 = "T19" T19 = "T19"
S21 = "S21" S21 = "S21"
S21Plus = "S21+"
S21PlusHydro = "S21+ Hydro"
S21Pro = "S21 Pro" S21Pro = "S21 Pro"
S21Hydro = "S21 Hydro"
T21 = "T21" T21 = "T21"
def __str__(self): def __str__(self):
@@ -446,7 +450,9 @@ class AvalonminerModels(MinerModelType):
Avalon1166Pro = "Avalon 1166 Pro" Avalon1166Pro = "Avalon 1166 Pro"
Avalon1126Pro = "Avalon 1126 Pro" Avalon1126Pro = "Avalon 1126 Pro"
Avalon1246 = "Avalon 1246" Avalon1246 = "Avalon 1246"
Avalon1566 = "Avalon 1566"
AvalonNano3 = "Avalon Nano 3" AvalonNano3 = "Avalon Nano 3"
AvalonNano3s = "Avalon Nano 3s"
def __str__(self): def __str__(self):
return self.value return self.value
@@ -477,7 +483,9 @@ class GoldshellModels(MinerModelType):
class ePICModels(MinerModelType): class ePICModels(MinerModelType):
BM520i = "BlockMiner 520i" BM520i = "BlockMiner 520i"
BM720i = "BlockMiner 720i" BM720i = "BlockMiner 720i"
eLITE1 = "BlockMiner eLITE 1.0"
S19jProDual = "S19j Pro Dual" S19jProDual = "S19j Pro Dual"
S19kProDual = "S19k Pro Dual"
def __str__(self): def __str__(self):
return self.value return self.value
@@ -506,6 +514,14 @@ class BitAxeModels(MinerModelType):
return self.value return self.value
class LuckyMinerModels(MinerModelType):
LV07 = "LV07"
LV08 = "LV08"
def __str__(self):
return self.value
class IceRiverModels(MinerModelType): class IceRiverModels(MinerModelType):
KS0 = "KS0" KS0 = "KS0"
KS1 = "KS1" KS1 = "KS1"
@@ -540,6 +556,10 @@ class BraiinsModels(MinerModelType):
BMM101 = "BMM101" BMM101 = "BMM101"
class ElphapexModels(MinerModelType):
DG1Plus = "DG1+"
class MinerModel: class MinerModel:
ANTMINER = AntminerModels ANTMINER = AntminerModels
WHATSMINER = WhatsminerModels WHATSMINER = WhatsminerModels
@@ -549,7 +569,9 @@ class MinerModel:
AURADINE = AuradineModels AURADINE = AuradineModels
EPIC = ePICModels EPIC = ePICModels
BITAXE = BitAxeModels BITAXE = BitAxeModels
LUCKYMINER = LuckyMinerModels
ICERIVER = IceRiverModels ICERIVER = IceRiverModels
HAMMER = HammerModels HAMMER = HammerModels
VOLCMINER = VolcMinerModels VOLCMINER = VolcMinerModels
ELPHAPEX = ElphapexModels
BRAIINS = BraiinsModels BRAIINS = BraiinsModels

View File

@@ -21,4 +21,5 @@ from .epic import *
from .hiveon import * from .hiveon import *
from .luxos import * from .luxos import *
from .marathon import * from .marathon import *
from .mskminer import *
from .vnish import * from .vnish import *

View File

@@ -15,12 +15,24 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import AntminerModern from pyasic.miners.backends import AntminerModern
from pyasic.miners.device.models import S21, S21Pro from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro
class BMMinerS21(AntminerModern, S21): class BMMinerS21(AntminerModern, S21):
pass pass
class BMMinerS21Plus(AntminerModern, S21Plus):
pass
class BMMinerS21PlusHydro(AntminerModern, S21PlusHydro):
pass
class BMMinerS21Pro(AntminerModern, S21Pro): class BMMinerS21Pro(AntminerModern, S21Pro):
pass pass
class BMMinerS21Hydro(AntminerModern, S21Hydro):
pass

View File

@@ -13,5 +13,11 @@
# 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 .S21 import BMMinerS21, BMMinerS21Pro from .S21 import (
BMMinerS21,
BMMinerS21Hydro,
BMMinerS21Plus,
BMMinerS21PlusHydro,
BMMinerS21Pro,
)
from .T21 import BMMinerT21 from .T21 import BMMinerT21

View File

@@ -14,8 +14,12 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import AntminerModern from pyasic.miners.backends import AntminerModern
from pyasic.miners.device.models import KS5 from pyasic.miners.device.models.antminer import KS5, KS5Pro
class BMMinerKS5(AntminerModern, KS5): class BMMinerKS5(AntminerModern, KS5):
supports_shutdown = False supports_shutdown = False
class BMMinerKS5Pro(AntminerModern, KS5Pro):
supports_shutdown = False

View File

@@ -13,4 +13,4 @@
# 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 .KS5 import BMMinerKS5 from .KS5 import BMMinerKS5, BMMinerKS5Pro

View File

@@ -15,8 +15,40 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSer from pyasic.miners.backends import BOSer
from pyasic.miners.device.models import S21 from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro
# ------------------------------------------------------------------------------
# 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. -
# ------------------------------------------------------------------------------
class BOSMinerS21(BOSer, S21): class BOSMinerS21(BOSer, S21):
pass pass
class BOSMinerS21Plus(BOSer, S21Plus):
pass
class BOSMinerS21PlusHydro(BOSer, S21PlusHydro):
pass
class BOSMinerS21Pro(BOSer, S21Pro):
pass
class BOSMinerS21Hydro(BOSer, S21Hydro):
pass

View File

@@ -14,5 +14,11 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .S21 import BOSMinerS21 from .S21 import (
BOSMinerS21,
BOSMinerS21Hydro,
BOSMinerS21Plus,
BOSMinerS21PlusHydro,
BOSMinerS21Pro,
)
from .T21 import BOSMinerT21 from .T21 import BOSMinerT21

View File

@@ -59,3 +59,9 @@ class ePICS19jProDual(ePIC, S19jPro):
raw_model = MinerModel.EPIC.S19jProDual raw_model = MinerModel.EPIC.S19jProDual
expected_fans = S19jPro.expected_fans * 2 expected_fans = S19jPro.expected_fans * 2
expected_hashboards = S19jPro.expected_hashboards * 2 expected_hashboards = S19jPro.expected_hashboards * 2
class ePICS19kProDual(ePIC, S19kPro):
raw_model = MinerModel.EPIC.S19kProDual
expected_fans = S19kPro.expected_fans * 2
expected_hashboards = S19kPro.expected_hashboards * 2

View File

@@ -21,6 +21,7 @@ from .S19 import (
ePICS19jProDual, ePICS19jProDual,
ePICS19jProPlus, ePICS19jProPlus,
ePICS19kPro, ePICS19kPro,
ePICS19kProDual,
ePICS19Pro, ePICS19Pro,
ePICS19XP, ePICS19XP,
) )

View File

@@ -27,6 +27,7 @@ from pyasic.miners.device.models import (
S19jNoPIC, S19jNoPIC,
S19jPro, S19jPro,
S19KPro, S19KPro,
S19NoPIC,
S19Plus, S19Plus,
S19Pro, S19Pro,
S19ProHydro, S19ProHydro,
@@ -39,6 +40,10 @@ class HiveonS19(HiveonModern, S19):
pass pass
class HiveonS19NoPIC(HiveonModern, S19NoPIC):
pass
class HiveonS19Plus(HiveonModern, S19Plus): class HiveonS19Plus(HiveonModern, S19Plus):
pass pass
@@ -95,5 +100,5 @@ class HiveonS19ProPlusHydro(HiveonModern, S19ProPlusHydro):
pass pass
class HiveonS19KPro(HiveonModern, S19KPro): class HiveonS19kPro(HiveonModern, S19KPro):
pass pass

View File

@@ -23,8 +23,9 @@ from .S19 import (
HiveonS19j, HiveonS19j,
HiveonS19jNoPIC, HiveonS19jNoPIC,
HiveonS19jPro, HiveonS19jPro,
HiveonS19KPro, HiveonS19kPro,
HiveonS19L, HiveonS19L,
HiveonS19NoPIC,
HiveonS19Plus, HiveonS19Plus,
HiveonS19Pro, HiveonS19Pro,
HiveonS19ProHydro, HiveonS19ProHydro,

View File

@@ -16,8 +16,6 @@
from typing import List, Optional from typing import List, Optional
import asyncssh
from pyasic.data import HashBoard from pyasic.data import HashBoard
from pyasic.device.algorithm import AlgoHashRate, HashUnit from pyasic.device.algorithm import AlgoHashRate, HashUnit
from pyasic.errors import APIError from pyasic.errors import APIError

View File

@@ -0,0 +1,22 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.miners.backends import LUXMiner
from pyasic.miners.device.models import S21, T21
class LUXMinerT21(LUXMiner, T21):
pass

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .S21 import LUXMinerS21 from .S21 import LUXMinerS21
from .T21 import LUXMinerT21

View File

@@ -0,0 +1,24 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.miners.backends.mskminer import MSKMiner
from pyasic.miners.device.models import (
S19NoPIC,
)
class MSKMinerS19NoPIC(MSKMiner, S19NoPIC):
pass

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# 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 .S19 import MSKMinerS19NoPIC

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# 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 .X19 import *

View File

@@ -20,6 +20,7 @@ from pyasic.miners.device.models import (
S19XP, S19XP,
S19a, S19a,
S19aPro, S19aPro,
S19i,
S19j, S19j,
S19jPro, S19jPro,
S19kPro, S19kPro,
@@ -53,6 +54,10 @@ class VNishS19aPro(VNish, S19aPro):
pass pass
class VNishS19i(VNish, S19i):
pass
class VNishS19j(VNish, S19j): class VNishS19j(VNish, S19j):
pass pass

View File

@@ -18,6 +18,7 @@ from .S19 import (
VNishS19, VNishS19,
VNishS19a, VNishS19a,
VNishS19aPro, VNishS19aPro,
VNishS19i,
VNishS19j, VNishS19j,
VNishS19jPro, VNishS19jPro,
VNishS19kPro, VNishS19kPro,

View File

@@ -15,8 +15,24 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import VNish from pyasic.miners.backends import VNish
from pyasic.miners.device.models import S21 from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro
class VNishS21(VNish, S21): class VNishS21(VNish, S21):
pass pass
class VNishS21Plus(VNish, S21Plus):
pass
class VNishS21PlusHydro(VNish, S21PlusHydro):
pass
class VNishS21Pro(VNish, S21Pro):
pass
class VNishS21Hydro(VNish, S21Hydro):
pass

View File

@@ -0,0 +1,22 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.miners.backends import VNish
from pyasic.miners.device.models import T21
class VNishT21(VNish, T21):
pass

View File

@@ -14,4 +14,11 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .S21 import VNishS21 from .S21 import (
VNishS21,
VNishS21Hydro,
VNishS21Plus,
VNishS21PlusHydro,
VNishS21Pro,
)
from .T21 import VNishT21

View File

@@ -0,0 +1,22 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.miners.backends import AvalonMiner
from pyasic.miners.device.models import Avalon1566
class CGMinerAvalon1566(AvalonMiner, Avalon1566):
pass

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# 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 .A1566 import CGMinerAvalon1566

View File

@@ -20,4 +20,5 @@ from .A9X import *
from .A10X import * from .A10X import *
from .A11X import * from .A11X import *
from .A12X import * from .A12X import *
from .A15X import *
from .nano import * from .nano import *

View File

@@ -14,4 +14,4 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .nano3 import CGMinerAvalonNano3 from .nano3 import CGMinerAvalonNano3, CGMinerAvalonNano3s

View File

@@ -13,10 +13,219 @@
# 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 typing import List, Optional
from pyasic import APIError
from pyasic.data.boards import HashBoard
from pyasic.device.algorithm.hashrate import AlgoHashRate
from pyasic.miners.backends import AvalonMiner from pyasic.miners.backends import AvalonMiner
from pyasic.miners.device.models import AvalonNano3 from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.miners.device.models import AvalonNano3, AvalonNano3s
from pyasic.web.avalonminer import AvalonMinerWebAPI
AVALON_NANO_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_minerinfo", "get_minerinfo")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_devs", "devs")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"_get_env_temp",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
AVALON_NANO3S_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"_get_mac",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"_get_env_temp",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
class CGMinerAvalonNano3(AvalonMiner, AvalonNano3): class CGMinerAvalonNano3(AvalonMiner, AvalonNano3):
_web_cls = AvalonMinerWebAPI
web: AvalonMinerWebAPI
data_locations = AVALON_NANO_DATA_LOC
async def _get_mac(self, web_minerinfo: dict) -> Optional[dict]:
if web_minerinfo is None:
try:
web_minerinfo = await self.web.minerinfo()
except APIError:
pass pass
if web_minerinfo is not None:
try:
mac = web_minerinfo.get("mac")
if mac is not None:
return mac.upper()
except (KeyError, ValueError):
pass
class CGMinerAvalonNano3s(AvalonMiner, AvalonNano3s):
data_locations = AVALON_NANO3S_DATA_LOC
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["PS"][6])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_hashrate(self, rpc_stats: dict = None) -> Optional[AlgoHashRate]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return self.algo.hashrate(
rate=float(parsed_stats["GHSspd"][0]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = await AvalonMiner._get_hashboards(self, rpc_stats)
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
except (IndexError, KeyError, ValueError, TypeError):
return hashboards
for board in range(len(hashboards)):
try:
board_hr = parsed_stats["GHSspd"][board]
hashboards[board].hashrate = self.algo.hashrate(
rate=float(board_hr), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except LookupError:
pass
return hashboards

View File

@@ -22,14 +22,17 @@ from .bmminer import BMMiner
from .braiins_os import BOSer, BOSMiner from .braiins_os import BOSer, BOSMiner
from .btminer import BTMiner from .btminer import BTMiner
from .cgminer import CGMiner from .cgminer import CGMiner
from .elphapex import ElphapexMiner
from .epic import ePIC from .epic import ePIC
from .goldshell import GoldshellMiner from .goldshell import GoldshellMiner
from .hammer import BlackMiner from .hammer import BlackMiner
from .hiveon import HiveonModern, HiveonOld from .hiveon import HiveonModern, HiveonOld
from .iceriver import IceRiver from .iceriver import IceRiver
from .innosilicon import Innosilicon from .innosilicon import Innosilicon
from .luckyminer import LuckyMiner
from .luxminer import LUXMiner from .luxminer import LUXMiner
from .marathon import MaraMiner from .marathon import MaraMiner
from .mskminer import MSKMiner
from .unknown import UnknownMiner from .unknown import UnknownMiner
from .vnish import VNish from .vnish import VNish
from .whatsminer import M2X, M3X, M5X, M6X, M7X from .whatsminer import M2X, M3X, M5X, M6X, M7X

View File

@@ -75,6 +75,10 @@ ANTMINER_MODERN_DATA_LOC = DataLocations(
"_get_fault_light", "_get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")], [WebAPICommand("web_get_blink_status", "get_blink_status")],
), ),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[],
),
str(DataOptions.IS_MINING): DataFunction( str(DataOptions.IS_MINING): DataFunction(
"_is_mining", "_is_mining",
[WebAPICommand("web_get_conf", "get_miner_conf")], [WebAPICommand("web_get_conf", "get_miner_conf")],
@@ -248,6 +252,9 @@ class AntminerModern(BMMiner):
return errors return errors
async def _get_hashboards(self) -> List[HashBoard]: async def _get_hashboards(self) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=idx, expected_chips=self.expected_chips) HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards) for idx in range(self.expected_hashboards)
@@ -265,18 +272,47 @@ class AntminerModern(BMMiner):
rate=board["rate_real"], unit=self.algo.unit.GH rate=board["rate_real"], unit=self.algo.unit.GH
).into(self.algo.unit.default) ).into(self.algo.unit.default)
hashboards[board["index"]].chips = board["asic_num"] hashboards[board["index"]].chips = board["asic_num"]
if "S21+ Hyd" in self.model:
hashboards[board["index"]].inlet_temp = board["temp_pcb"][0]
hashboards[board["index"]].outlet_temp = board["temp_pcb"][2]
hashboards[board["index"]].chip_temp = board["temp_pic"][0]
board_temp_data = list(
filter(
lambda x: not x == 0,
[
board["temp_pic"][1],
board["temp_pic"][2],
board["temp_pic"][3],
board["temp_pcb"][1],
board["temp_pcb"][3],
],
)
)
hashboards[board["index"]].temp = (
sum(board_temp_data) / len(board_temp_data)
if len(board_temp_data) > 0
else 0
)
else:
board_temp_data = list( board_temp_data = list(
filter(lambda x: not x == 0, board["temp_pcb"]) filter(lambda x: not x == 0, board["temp_pcb"])
) )
hashboards[board["index"]].temp = sum(board_temp_data) / len( hashboards[board["index"]].temp = (
board_temp_data sum(board_temp_data) / len(board_temp_data)
if len(board_temp_data) > 0
else 0
) )
chip_temp_data = list( chip_temp_data = list(
filter(lambda x: not x == 0, board["temp_chip"]) filter(lambda x: not x == 0, board["temp_chip"])
) )
hashboards[board["index"]].chip_temp = sum(chip_temp_data) / len( hashboards[board["index"]].chip_temp = (
chip_temp_data sum(chip_temp_data) / len(chip_temp_data)
if len(chip_temp_data) > 0
else 0
) )
hashboards[board["index"]].serial_number = board["sn"] hashboards[board["index"]].serial_number = board["sn"]
hashboards[board["index"]].missing = False hashboards[board["index"]].missing = False
except LookupError: except LookupError:
@@ -561,6 +597,9 @@ class AntminerOld(CGMiner):
pass pass
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]: async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None: if rpc_stats is None:
try: try:
rpc_stats = await self.rpc.stats() rpc_stats = await self.rpc.stats()
@@ -589,6 +628,8 @@ class AntminerOld(CGMiner):
return fans_data return fans_data
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [] hashboards = []
if rpc_stats is None: if rpc_stats is None:

View File

@@ -303,6 +303,9 @@ class Auradine(StockFirmware):
async def _get_hashboards( async def _get_hashboards(
self, rpc_devs: dict = None, web_ipreport: dict = None self, rpc_devs: dict = None, web_ipreport: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
@@ -382,6 +385,9 @@ class Auradine(StockFirmware):
pass pass
async def _get_fans(self, web_fan: dict = None) -> List[Fan]: async def _get_fans(self, web_fan: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_fan is None: if web_fan is None:
try: try:
web_fan = await self.web.get_fan() web_fan = await self.web.get_fan()

View File

@@ -57,6 +57,10 @@ AVALON_DATA_LOC = DataLocations(
"_get_wattage_limit", "_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")], [RPCAPICommand("rpc_stats", "stats")],
), ),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction( str(DataOptions.FANS): DataFunction(
"_get_fans", "_get_fans",
[RPCAPICommand("rpc_stats", "stats")], [RPCAPICommand("rpc_stats", "stats")],
@@ -100,6 +104,23 @@ class AvalonMiner(CGMiner):
return True return True
return False return False
async def set_power_limit(self, wattage: int) -> bool:
try:
if wattage < 3:
limit = wattage
elif wattage > 100:
limit = 2
elif wattage > 80:
limit = 1
else:
limit = 0
data = await self.rpc.ascset(0, "worklevel,set", 1)
except APIError:
return False
if data["STATUS"][0]["Msg"] == "ASC 0 set OK":
return True
return False
async def reboot(self) -> bool: async def reboot(self) -> bool:
try: try:
data = await self.rpc.restart() data = await self.rpc.restart()
@@ -191,6 +212,9 @@ class AvalonMiner(CGMiner):
pass pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
@@ -289,7 +313,25 @@ class AvalonMiner(CGMiner):
except (IndexError, KeyError, ValueError, TypeError): except (IndexError, KeyError, ValueError, TypeError):
pass pass
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["WALLPOWER"][0])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]: async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None: if rpc_stats is None:
try: try:
rpc_stats = await self.rpc.stats() rpc_stats = await self.rpc.stats()

View File

@@ -129,6 +129,9 @@ class BFGMiner(StockFirmware):
pass pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [] hashboards = []
if rpc_stats is None: if rpc_stats is None:
@@ -185,6 +188,9 @@ class BFGMiner(StockFirmware):
return hashboards return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]: async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None: if rpc_stats is None:
try: try:
rpc_stats = await self.rpc.stats() rpc_stats = await self.rpc.stats()

View File

@@ -1,235 +1,7 @@
from typing import List, Optional from pyasic.miners.backends.espminer import ESPMiner
from pyasic import APIError, MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.device.algorithm import AlgoHashRate
from pyasic.device.firmware import MinerFirmware
from pyasic.miners.base import BaseMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.web.bitaxe import BitAxeWebAPI
BITAXE_DATA_LOC = DataLocations(
**{
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.HOSTNAME): DataFunction(
"_get_hostname",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_system_info", "system/info")],
),
}
)
class BitAxe(BaseMiner): class BitAxe(ESPMiner):
"""Handler for BitAxe""" """Handler for BitAxe"""
web: BitAxeWebAPI
_web_cls = BitAxeWebAPI
firmware = MinerFirmware.STOCK
data_locations = BITAXE_DATA_LOC
async def reboot(self) -> bool:
await self.web.restart()
return True
async def get_config(self) -> MinerConfig:
web_system_info = await self.web.system_info()
return MinerConfig.from_bitaxe(web_system_info)
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
await self.web.update_settings(**config.as_bitaxe())
async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return round(web_system_info["power"])
except KeyError:
pass
async def _get_hashrate(
self, web_system_info: dict = None
) -> Optional[AlgoHashRate]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return self.algo.hashrate(
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except KeyError:
pass
async def _get_expected_hashrate(
self, web_system_info: dict = None
) -> Optional[AlgoHashRate]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
expected_hashrate = (
web_system_info.get("smallCoreCount")
* web_system_info.get("asicCount")
* web_system_info.get("frequency")
)
return self.algo.hashrate(
rate=float(expected_hashrate), unit=self.algo.unit.MH
).into(self.algo.unit.default)
except KeyError:
pass
async def _get_uptime(self, web_system_info: dict = None) -> Optional[int]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["uptimeSeconds"]
except KeyError:
pass
async def _get_hashboards(self, web_system_info: dict = None) -> List[HashBoard]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return [
HashBoard(
hashrate=self.algo.hashrate(
rate=float(web_system_info["hashRate"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default),
chip_temp=web_system_info.get("temp"),
temp=web_system_info.get("vrTemp"),
chips=web_system_info.get("asicCount", 1),
expected_chips=self.expected_chips,
missing=False,
active=True,
voltage=web_system_info.get("voltage"),
)
]
except KeyError:
pass
return []
async def _get_fans(self, web_system_info: dict = None) -> List[Fan]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return [Fan(speed=web_system_info["fanrpm"])]
except KeyError:
pass
return []
async def _get_hostname(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["hostname"]
except KeyError:
pass
async def _get_api_ver(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["version"]
except KeyError:
pass
async def _get_fw_ver(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["version"]
except KeyError:
pass
async def _get_mac(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["macAddr"].upper()
except KeyError:
pass pass

View File

@@ -133,6 +133,9 @@ class BMMiner(StockFirmware):
pass pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [] hashboards = []
if rpc_stats is None: if rpc_stats is None:
@@ -202,6 +205,9 @@ class BMMiner(StockFirmware):
return hashboards return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]: async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None: if rpc_stats is None:
try: try:
rpc_stats = await self.rpc.stats() rpc_stats = await self.rpc.stats()

View File

@@ -376,6 +376,9 @@ class BOSMiner(BraiinsOSFirmware):
rpc_devdetails: dict = None, rpc_devdetails: dict = None,
rpc_devs: dict = None, rpc_devs: dict = None,
) -> List[HashBoard]: ) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
@@ -473,6 +476,9 @@ class BOSMiner(BraiinsOSFirmware):
pass pass
async def _get_fans(self, rpc_fans: dict = None) -> List[Fan]: async def _get_fans(self, rpc_fans: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_fans is None: if rpc_fans is None:
try: try:
rpc_fans = await self.rpc.fans() rpc_fans = await self.rpc.fans()
@@ -926,6 +932,9 @@ class BOSer(BraiinsOSFirmware):
pass pass
async def _get_hashboards(self, grpc_hashboards: dict = None) -> List[HashBoard]: async def _get_hashboards(self, grpc_hashboards: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
@@ -997,6 +1006,9 @@ class BOSer(BraiinsOSFirmware):
pass pass
async def _get_fans(self, grpc_cooling_state: dict = None) -> List[Fan]: async def _get_fans(self, grpc_cooling_state: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if grpc_cooling_state is None: if grpc_cooling_state is None:
try: try:
grpc_cooling_state = await self.web.get_cooling_state() grpc_cooling_state = await self.web.get_cooling_state()

View File

@@ -412,6 +412,9 @@ class BTMiner(StockFirmware):
pass pass
async def _get_hashboards(self, rpc_devs: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_devs: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
@@ -490,6 +493,9 @@ class BTMiner(StockFirmware):
async def _get_fans( async def _get_fans(
self, rpc_summary: dict = None, rpc_get_psu: dict = None self, rpc_summary: dict = None, rpc_get_psu: dict = None
) -> List[Fan]: ) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_summary is None: if rpc_summary is None:
try: try:
rpc_summary = await self.rpc.summary() rpc_summary = await self.rpc.summary()

View File

@@ -0,0 +1,380 @@
# ------------------------------------------------------------------------------
# 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 typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import Fan, HashBoard, X19Error
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
WebAPICommand,
)
from pyasic.miners.device.firmware import StockFirmware
from pyasic.web.elphapex import ElphapexWebAPI
ELPHAPEX_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.HOSTNAME): DataFunction(
"_get_hostname",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[WebAPICommand("web_stats", "stats")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[WebAPICommand("web_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_stats", "stats")],
),
str(DataOptions.ERRORS): DataFunction(
"_get_errors",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.IS_MINING): DataFunction(
"_is_mining",
[WebAPICommand("web_get_miner_conf", "get_miner_conf")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("web_pools", "pools")],
),
}
)
class ElphapexMiner(StockFirmware):
"""Handler for Elphapex miners."""
_web_cls = ElphapexWebAPI
web: ElphapexWebAPI
data_locations = ELPHAPEX_DATA_LOC
async def get_config(self) -> MinerConfig:
data = await self.web.get_miner_conf()
if data:
self.config = MinerConfig.from_elphapex(data)
return self.config
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config
await self.web.set_miner_conf(config.as_elphapex(user_suffix=user_suffix))
async def fault_light_on(self) -> bool:
data = await self.web.blink(blink=True)
if data:
if data.get("code") == "B000":
self.light = True
return self.light
async def fault_light_off(self) -> bool:
data = await self.web.blink(blink=False)
if data:
if data.get("code") == "B100":
self.light = False
return self.light
async def reboot(self) -> bool:
data = await self.web.reboot()
if data:
return True
return False
async def _get_api_ver(self, web_summary: dict = None) -> Optional[str]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
self.api_ver = web_summary["STATUS"]["api_version"]
except LookupError:
pass
return self.api_ver
async def _get_fw_ver(self, web_get_system_info: dict = None) -> Optional[str]:
if web_get_system_info is None:
try:
web_get_system_info = await self.web.get_system_info()
except APIError:
pass
if web_get_system_info is not None:
try:
self.fw_ver = (
web_get_system_info["system_filesystem_version"]
.upper()
.split("V")[-1]
)
except LookupError:
pass
return self.fw_ver
async def _get_hostname(self, web_get_system_info: dict = None) -> Optional[str]:
if web_get_system_info is None:
try:
web_get_system_info = await self.web.get_system_info()
except APIError:
pass
if web_get_system_info is not None:
try:
return web_get_system_info["hostname"]
except KeyError:
pass
async def _get_mac(self, web_get_system_info: dict = None) -> Optional[str]:
if web_get_system_info is None:
try:
web_get_system_info = await self.web.get_system_info()
except APIError:
pass
if web_get_system_info is not None:
try:
return web_get_system_info["macaddr"]
except KeyError:
pass
try:
data = await self.web.get_network_info()
if data:
return data["macaddr"]
except KeyError:
pass
async def _get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
errors = []
if web_summary is not None:
try:
for item in web_summary["SUMMARY"][0]["status"]:
try:
if not item["status"] == "s":
errors.append(X19Error(error_message=item["msg"]))
except KeyError:
continue
except LookupError:
pass
return errors
async def _get_hashboards(self, web_stats: dict | None = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
if web_stats is None:
try:
web_stats = await self.web.stats()
except APIError:
return hashboards
if web_stats is not None:
try:
for board in web_stats["STATS"][0]["chain"]:
hashboards[board["index"]].hashrate = self.algo.hashrate(
rate=board["rate_real"], unit=self.algo.unit.MH
).into(self.algo.unit.default)
hashboards[board["index"]].chips = board["asic_num"]
board_temp_data = list(
filter(lambda x: not x == 0, board["temp_pcb"])
)
hashboards[board["index"]].temp = sum(board_temp_data) / len(
board_temp_data
)
chip_temp_data = list(
filter(lambda x: not x == "", board["temp_chip"])
)
hashboards[board["index"]].chip_temp = sum(
[int(i) / 1000 for i in chip_temp_data]
) / len(chip_temp_data)
hashboards[board["index"]].serial_number = board["sn"]
hashboards[board["index"]].missing = False
except LookupError:
pass
return hashboards
async def _get_fault_light(
self, web_get_blink_status: dict = None
) -> Optional[bool]:
if self.light:
return self.light
if web_get_blink_status is None:
try:
web_get_blink_status = await self.web.get_blink_status()
except APIError:
pass
if web_get_blink_status is not None:
try:
self.light = web_get_blink_status["blink"]
except KeyError:
pass
return self.light
async def _get_expected_hashrate(
self, web_stats: dict = None
) -> Optional[AlgoHashRate]:
if web_stats is None:
try:
web_stats = await self.web.stats()
except APIError:
pass
if web_stats is not None:
try:
expected_rate = web_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = web_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "MH"
return self.algo.hashrate(
rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass
async def _is_mining(self, web_get_miner_conf: dict = None) -> Optional[bool]:
if web_get_miner_conf is None:
try:
web_get_miner_conf = await self.web.get_miner_conf()
except APIError:
pass
if web_get_miner_conf is not None:
try:
if str(web_get_miner_conf["fc-work-mode"]).isdigit():
return (
False if int(web_get_miner_conf["fc-work-mode"]) == 1 else True
)
return False
except LookupError:
pass
async def _get_uptime(self, web_summary: dict = None) -> Optional[int]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
return int(web_summary["SUMMARY"][1]["elapsed"])
except LookupError:
pass
async def _get_fans(self, web_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_stats is None:
try:
web_stats = await self.web.stats()
except APIError:
pass
fans = [Fan() for _ in range(self.expected_fans)]
if web_stats is not None:
for fan_n in range(self.expected_fans):
try:
fans[fan_n].speed = int(web_stats["STATS"][0]["fan"][fan_n])
except LookupError:
pass
return fans
async def _get_pools(self, web_pools: list = None) -> List[PoolMetrics]:
if web_pools is None:
try:
web_pools = await self.web.pools()
except APIError:
return []
active_pool_index = None
highest_priority = float("inf")
for pool_info in web_pools["POOLS"]:
if (
pool_info.get("status") == "Alive"
and pool_info.get("priority", float("inf")) < highest_priority
):
highest_priority = pool_info["priority"]
active_pool_index = pool_info["index"]
pools_data = []
if web_pools is not None:
try:
for pool_info in web_pools["POOLS"]:
url = pool_info.get("url")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("accepted"),
rejected=pool_info.get("rejected"),
get_failures=pool_info.get("stale"),
remote_failures=pool_info.get("discarded"),
active=pool_info.get("index") == active_pool_index,
alive=pool_info.get("status") == "Alive",
url=pool_url,
user=pool_info.get("user"),
index=pool_info.get("index"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -21,7 +21,7 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.data.pools import PoolMetrics, PoolUrl from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate from pyasic.device.algorithm import AlgoHashRate, ScryptAlgo
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger from pyasic.logger import logger
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
@@ -138,6 +138,8 @@ class ePIC(ePICFirmware):
await self.web.set_ptune_algo(conf["ptune"]) await self.web.set_ptune_algo(conf["ptune"])
## Pools ## Pools
if self.algo == ScryptAlgo:
conf["pools"]["coin"] = "Ltc"
await self.web.set_pools(conf["pools"]) await self.web.set_pools(conf["pools"])
except APIError: except APIError:
pass pass
@@ -283,6 +285,9 @@ class ePIC(ePICFirmware):
pass pass
async def _get_fans(self, web_summary: dict = None) -> List[Fan]: async def _get_fans(self, web_summary: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_summary is None: if web_summary is None:
try: try:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -302,6 +307,9 @@ class ePIC(ePICFirmware):
async def _get_hashboards( async def _get_hashboards(
self, web_summary: dict = None, web_capabilities: dict = None self, web_summary: dict = None, web_capabilities: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_summary is None: if web_summary is None:
try: try:
web_summary = await self.web.summary() web_summary = await self.web.summary()

View File

@@ -0,0 +1,248 @@
from typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.device.algorithm import AlgoHashRate
from pyasic.device.firmware import MinerFirmware
from pyasic.miners.base import BaseMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.web.espminer import ESPMinerWebAPI
ESPMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.HOSTNAME): DataFunction(
"_get_hostname",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_system_info", "system/info")],
),
}
)
class ESPMiner(BaseMiner):
"""Handler for ESPMiner"""
web: ESPMinerWebAPI
_web_cls = ESPMinerWebAPI
firmware = MinerFirmware.STOCK
data_locations = ESPMINER_DATA_LOC
async def reboot(self) -> bool:
await self.web.restart()
return True
async def get_config(self) -> MinerConfig:
web_system_info = await self.web.system_info()
return MinerConfig.from_espminer(web_system_info)
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
await self.web.update_settings(**config.as_espminer())
async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return round(web_system_info["power"])
except KeyError:
pass
async def _get_hashrate(
self, web_system_info: dict = None
) -> Optional[AlgoHashRate]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return self.algo.hashrate(
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except KeyError:
pass
async def _get_expected_hashrate(
self, web_system_info: dict = None
) -> Optional[AlgoHashRate]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
small_core_count = web_system_info.get("smallCoreCount")
asic_count = web_system_info.get("asicCount")
frequency = web_system_info.get("frequency")
if asic_count is None:
try:
asic_info = await self.web.asic_info()
asic_count = asic_info.get("asicCount")
except APIError:
pass
expected_hashrate = small_core_count * asic_count * frequency
return self.algo.hashrate(
rate=float(expected_hashrate), unit=self.algo.unit.MH
).into(self.algo.unit.default)
except KeyError:
pass
async def _get_uptime(self, web_system_info: dict = None) -> Optional[int]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["uptimeSeconds"]
except KeyError:
pass
async def _get_hashboards(self, web_system_info: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return [
HashBoard(
hashrate=self.algo.hashrate(
rate=float(web_system_info["hashRate"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default),
chip_temp=web_system_info.get("temp"),
temp=web_system_info.get("vrTemp"),
chips=web_system_info.get("asicCount", 1),
expected_chips=self.expected_chips,
missing=False,
active=True,
voltage=web_system_info.get("voltage"),
)
]
except KeyError:
pass
return []
async def _get_fans(self, web_system_info: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return [Fan(speed=web_system_info["fanrpm"])]
except KeyError:
pass
return []
async def _get_hostname(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["hostname"]
except KeyError:
pass
async def _get_api_ver(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["version"]
except KeyError:
pass
async def _get_fw_ver(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["version"]
except KeyError:
pass
async def _get_mac(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["macAddr"].upper()
except KeyError:
pass

View File

@@ -145,6 +145,9 @@ class GoldshellMiner(BFGMiner):
async def _get_hashboards( async def _get_hashboards(
self, rpc_devs: dict = None, rpc_devdetails: dict = None self, rpc_devs: dict = None, rpc_devdetails: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if rpc_devs is None: if rpc_devs is None:
try: try:
rpc_devs = await self.rpc.devs() rpc_devs = await self.rpc.devs()

View File

@@ -180,6 +180,9 @@ class BlackMiner(StockFirmware):
pass pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [] hashboards = []
if rpc_stats is None: if rpc_stats is None:
@@ -245,6 +248,9 @@ class BlackMiner(StockFirmware):
return hashboards return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]: async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None: if rpc_stats is None:
try: try:
rpc_stats = await self.rpc.stats() rpc_stats = await self.rpc.stats()

View File

@@ -79,6 +79,9 @@ class IceRiver(StockFirmware):
return MinerConfig.from_iceriver(web_userpanel) return MinerConfig.from_iceriver(web_userpanel)
async def _get_fans(self, web_userpanel: dict = None) -> List[Fan]: async def _get_fans(self, web_userpanel: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_userpanel is None: if web_userpanel is None:
try: try:
web_userpanel = await self.web.userpanel() web_userpanel = await self.web.userpanel()
@@ -170,6 +173,9 @@ class IceRiver(StockFirmware):
pass pass
async def _get_hashboards(self, web_userpanel: dict = None) -> List[HashBoard]: async def _get_hashboards(self, web_userpanel: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_userpanel is None: if web_userpanel is None:
try: try:
web_userpanel = await self.web.userpanel() web_userpanel = await self.web.userpanel()

View File

@@ -211,6 +211,9 @@ class Innosilicon(CGMiner):
async def _get_hashboards( async def _get_hashboards(
self, rpc_stats: dict = None, web_get_all: dict = None self, rpc_stats: dict = None, web_get_all: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_get_all: if web_get_all:
web_get_all = web_get_all["all"] web_get_all = web_get_all["all"]
@@ -304,6 +307,9 @@ class Innosilicon(CGMiner):
return wattage return wattage
async def _get_fans(self, web_get_all: dict = None) -> List[Fan]: async def _get_fans(self, web_get_all: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_get_all: if web_get_all:
web_get_all = web_get_all["all"] web_get_all = web_get_all["all"]

View File

@@ -0,0 +1,7 @@
from pyasic.miners.backends.espminer import ESPMiner
class LuckyMiner(ESPMiner):
"""Handler for LuckyMiner"""
pass

View File

@@ -17,6 +17,7 @@ import logging
from typing import List, Optional from typing import List, Optional
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePreset
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate from pyasic.device.algorithm import AlgoHashRate
@@ -47,6 +48,13 @@ LUXMINER_DATA_LOC = DataLocations(
"_get_wattage", "_get_wattage",
[RPCAPICommand("rpc_power", "power")], [RPCAPICommand("rpc_power", "power")],
), ),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[
RPCAPICommand("rpc_config", "config"),
RPCAPICommand("rpc_profiles", "profiles"),
],
),
str(DataOptions.FANS): DataFunction( str(DataOptions.FANS): DataFunction(
"_get_fans", "_get_fans",
[RPCAPICommand("rpc_fans", "fans")], [RPCAPICommand("rpc_fans", "fans")],
@@ -78,6 +86,7 @@ class LUXMiner(LuxOSFirmware):
supports_shutdown = True supports_shutdown = True
supports_presets = True supports_presets = True
supports_autotuning = True
data_locations = LUXMINER_DATA_LOC data_locations = LUXMINER_DATA_LOC
@@ -144,14 +153,14 @@ class LUXMiner(LuxOSFirmware):
rpc_profiles=data.get("profiles", [{}])[0], rpc_profiles=data.get("profiles", [{}])[0],
) )
async def upgrade_firmware(self) -> bool: async def upgrade_firmware(self, *args, **kwargs) -> bool:
""" """
Upgrade the firmware on a LuxOS miner by calling the 'updaterun' API command. Upgrade the firmware on a LuxOS miner by calling the 'updaterun' API command.
Returns: Returns:
bool: True if the firmware upgrade was successfully initiated, False otherwise. bool: True if the firmware upgrade was successfully initiated, False otherwise.
""" """
try: try:
await self.rpc.upgraderun() await self.rpc.updaterun()
logging.info(f"{self.ip}: Firmware upgrade initiated successfully.") logging.info(f"{self.ip}: Firmware upgrade initiated successfully.")
return True return True
@@ -183,10 +192,10 @@ class LUXMiner(LuxOSFirmware):
try: try:
if await self.atm_enabled(): if await self.atm_enabled():
re_enable_atm = True re_enable_atm = True
await self.rpc.atmset("enabled=false") await self.rpc.atmset(enabled=False)
result = await self.rpc.profileset(new_preset) result = await self.rpc.profileset(new_preset)
if re_enable_atm: if re_enable_atm:
await self.rpc.atmset("enabled=true") await self.rpc.atmset(enabled=True)
except APIError: except APIError:
raise raise
except Exception as e: except Exception as e:
@@ -232,6 +241,9 @@ class LUXMiner(LuxOSFirmware):
pass pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=idx, expected_chips=self.expected_chips) HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards) for idx in range(self.expected_hashboards)
@@ -289,7 +301,21 @@ class LUXMiner(LuxOSFirmware):
except (LookupError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
async def _get_wattage_limit(
self, rpc_config: dict = None, rpc_profiles: list[dict] = None
) -> Optional[int]:
try:
active_preset = MiningModePreset.get_active_preset_from_luxos(
rpc_config, rpc_profiles
)
return active_preset.power
except (LookupError, ValueError, TypeError):
pass
async def _get_fans(self, rpc_fans: dict = None) -> List[Fan]: async def _get_fans(self, rpc_fans: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_fans is None: if rpc_fans is None:
try: try:
rpc_fans = await self.rpc.fans() rpc_fans = await self.rpc.fans()

View File

@@ -164,6 +164,9 @@ class MaraMiner(MaraFirmware):
pass pass
async def _get_hashboards(self, web_hashboards: dict = None) -> List[HashBoard]: async def _get_hashboards(self, web_hashboards: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
@@ -250,6 +253,9 @@ class MaraMiner(MaraFirmware):
pass pass
async def _get_fans(self, web_fans: dict = None) -> List[Fan]: async def _get_fans(self, web_fans: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_fans is None: if web_fans is None:
try: try:
web_fans = await self.web.fans() web_fans = await self.web.fans()

View File

@@ -0,0 +1,110 @@
from typing import Optional
from pyasic import APIError
from pyasic.device.algorithm import AlgoHashRate
from pyasic.miners.backends import BMMiner
from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.mskminer import MSKMinerWebAPI
MSKMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_info_v1", "info_v1")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
class MSKMiner(BMMiner):
"""Handler for MSKMiner"""
data_locations = MSKMINER_DATA_LOC
web: MSKMinerWebAPI
_web_cls = MSKMinerWebAPI
async def _get_hashrate(self, rpc_stats: dict = None) -> Optional[AlgoHashRate]:
# get hr from API
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
return self.algo.hashrate(
rate=float(rpc_stats["STATS"][0]["total_rate"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
return rpc_stats["STATS"][0]["total_power"]
except (LookupError, ValueError, TypeError):
pass
async def _get_mac(self, web_info_v1: dict = None) -> Optional[str]:
if web_info_v1 is None:
try:
web_info_v1 = await self.web.info_v1()
except APIError:
pass
if web_info_v1 is not None:
try:
return web_info_v1["network_info"]["result"]["macaddr"].upper()
except (LookupError, ValueError, TypeError):
pass

View File

@@ -97,7 +97,7 @@ class UnknownMiner(BaseMiner):
return None return None
async def _get_fans(self) -> List[Fan]: async def _get_fans(self) -> List[Fan]:
return [Fan(), Fan(), Fan(), Fan()] return []
async def _get_fan_psu(self) -> Optional[int]: async def _get_fan_psu(self) -> Optional[int]:
return None return None

View File

@@ -15,9 +15,10 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging import logging
from typing import Optional from typing import List, Optional
from pyasic import MinerConfig from pyasic import MinerConfig
from pyasic.data.error_codes import MinerErrorData, VnishError
from pyasic.device.algorithm import AlgoHashRate from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.bmminer import BMMiner
@@ -85,6 +86,10 @@ VNISH_DATA_LOC = DataLocations(
"_get_pools", "_get_pools",
[RPCAPICommand("rpc_pools", "pools")], [RPCAPICommand("rpc_pools", "pools")],
), ),
str(DataOptions.ERRORS): DataFunction(
"_get_errors",
[WebAPICommand("web_summary", "summary")],
),
} }
) )
@@ -266,13 +271,34 @@ class VNish(VNishFirmware, BMMiner):
except LookupError: except LookupError:
pass pass
async def _get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
errors = []
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
return errors
if web_summary is not None:
chains = web_summary.get("miner", {}).get("chains", [])
for chain in chains:
state = chain.get("status", {}).get("state")
description = chain.get("status", {}).get("description", "")
if state == "failure":
errors.append(VnishError(error_message=description))
return errors
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
try: try:
web_settings = await self.web.settings() web_settings = await self.web.settings()
web_presets = await self.web.autotune_presets() web_presets = await self.web.autotune_presets()
web_perf_summary = (await self.web.perf_summary()) or {}
except APIError: except APIError:
return self.config return self.config
self.config = MinerConfig.from_vnish(web_settings, web_presets) self.config = MinerConfig.from_vnish(
web_settings, web_presets, web_perf_summary
)
return self.config return self.config
async def set_power_limit(self, wattage: int) -> bool: async def set_power_limit(self, wattage: int) -> bool:

View File

@@ -14,4 +14,4 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .blockminer import ePICBlockMiner520i, ePICBlockMiner720i from .blockminer import *

View File

@@ -15,7 +15,11 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import ePIC from pyasic.miners.backends import ePIC
from pyasic.miners.device.models import BlockMiner520i, BlockMiner720i from pyasic.miners.device.models import (
BlockMiner520i,
BlockMiner720i,
BlockMinerELITE1,
)
class ePICBlockMiner520i(ePIC, BlockMiner520i): class ePICBlockMiner520i(ePIC, BlockMiner520i):
@@ -24,3 +28,7 @@ class ePICBlockMiner520i(ePIC, BlockMiner520i):
class ePICBlockMiner720i(ePIC, BlockMiner720i): class ePICBlockMiner720i(ePIC, BlockMiner720i):
pass pass
class ePICBlockMinerELITE1(ePIC, BlockMinerELITE1):
pass

View File

@@ -50,6 +50,10 @@ class BitAxeMake(BaseMiner):
make = MinerMake.BITAXE make = MinerMake.BITAXE
class LuckyMinerMake(BaseMiner):
make = MinerMake.LUCKYMINER
class IceRiverMake(BaseMiner): class IceRiverMake(BaseMiner):
make = MinerMake.ICERIVER make = MinerMake.ICERIVER
@@ -64,3 +68,7 @@ class VolcMinerMake(BaseMiner):
class BraiinsMake(BaseMiner): class BraiinsMake(BaseMiner):
make = MinerMake.BRAIINS make = MinerMake.BRAIINS
class ElphapexMake(BaseMiner):
make = MinerMake.ELPHAPEX

View File

@@ -18,6 +18,7 @@ from .antminer import *
from .auradine import * from .auradine import *
from .avalonminer import * from .avalonminer import *
from .braiins import * from .braiins import *
from .elphapex import *
from .epic import * from .epic import *
from .goldshell import * from .goldshell import *
from .hammer import * from .hammer import *

View File

@@ -27,6 +27,24 @@ class S21(AntMinerMake):
algo = MinerAlgo.SHA256 algo = MinerAlgo.SHA256
class S21Plus(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21Plus
expected_chips = 55
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S21PlusHydro(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21PlusHydro
expected_chips = 95
expected_fans = 0
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S21Pro(AntMinerMake): class S21Pro(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21Pro raw_model = MinerModel.ANTMINER.S21Pro
@@ -34,3 +52,12 @@ class S21Pro(AntMinerMake):
expected_fans = 4 expected_fans = 4
expected_hashboards = 3 expected_hashboards = 3
algo = MinerAlgo.SHA256 algo = MinerAlgo.SHA256
class S21Hydro(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21Hydro
expected_chips = 216
expected_hashboards = 3
expected_fans = 0
algo = MinerAlgo.SHA256

View File

@@ -14,5 +14,5 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .S21 import S21, S21Pro from .S21 import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro
from .T21 import T21 from .T21 import T21

View File

@@ -25,3 +25,12 @@ class KS5(AntMinerMake):
expected_hashboards = 3 expected_hashboards = 3
expected_fans = 4 expected_fans = 4
algo = MinerAlgo.KHEAVYHASH algo = MinerAlgo.KHEAVYHASH
class KS5Pro(AntMinerMake):
raw_model = MinerModel.ANTMINER.KS5
expected_chips = 92
expected_hashboards = 3
expected_fans = 4
algo = MinerAlgo.KHEAVYHASH

View File

@@ -14,4 +14,4 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .DR5 import DR5 from .DR5 import DR5
from .KS5 import KS5 from .KS5 import KS5, KS5Pro

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import AvalonMinerMake
class Avalon1566(AvalonMinerMake):
raw_model = MinerModel.AVALONMINER.Avalon1566
expected_chips = 160
expected_fans = 2
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# 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 .A1566 import Avalon1566

View File

@@ -20,4 +20,5 @@ from .A9X import *
from .A10X import * from .A10X import *
from .A11X import * from .A11X import *
from .A12X import * from .A12X import *
from .A15X import *
from .nano import * from .nano import *

View File

@@ -1 +1 @@
from .nano3 import AvalonNano3 from .nano3 import AvalonNano3, AvalonNano3s

View File

@@ -10,3 +10,12 @@ class AvalonNano3(AvalonMinerMake):
expected_chips = 10 expected_chips = 10
expected_fans = 1 expected_fans = 1
algo = MinerAlgo.SHA256 algo = MinerAlgo.SHA256
class AvalonNano3s(AvalonMinerMake):
raw_model = MinerModel.AVALONMINER.AvalonNano3s
expected_hashboards = 1
expected_chips = 12
expected_fans = 1
algo = MinerAlgo.SHA256

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2024 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import ElphapexMake
class DG1Plus(ElphapexMake):
raw_model = MinerModel.ELPHAPEX.DG1Plus
expected_chips = 204
expected_hashboards = 4
expected_fans = 4
algo = MinerAlgo.SCRYPT

View File

@@ -0,0 +1 @@
from .DG1 import DG1Plus

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