Compare commits

...

152 Commits

Author SHA1 Message Date
pre-commit-ci[bot]
3e6067aef5 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.13.2 → v0.14.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.13.2...v0.14.4)
2025-11-10 20:25:43 +00:00
James Hilliard
1f4054bf38 feature: add mypy type checking for better type consistency 2025-09-26 11:24:38 -06:00
Brett Rowan
cd52d3aeaf version: bump version number 2025-09-23 12:40:02 -06:00
Brett Rowan
66c9b3663e feature: add serial number for antminers 2025-09-23 12:39:16 -06:00
James Hilliard
5f0e1da938 Update dependencies 2025-09-22 18:34:03 -06:00
Brett Rowan
2bd031b33d version: bump version number 2025-09-19 15:49:29 -06:00
James Hilliard
e2f07818cc Fix race conditions in RPC and web API multicommand methods
Multiple multicommand methods were double-awaiting tasks - first via
asyncio.gather() with return_exceptions=True, then calling .result() on
the same tasks. This caused ConnectionResetError and other exceptions
when connections were lost.

Changed to properly use the results from gather() instead of calling
.result() on completed tasks, preventing exceptions from being raised
after they were already caught.

Fixed in:
- pyasic/rpc/base.py:144 - RPC _send_split_multicommand
- pyasic/web/espminer.py:79 - ESPMiner multicommand
- pyasic/web/auradine.py:149 - Auradine multicommand
2025-09-19 14:31:33 -06:00
Brett Rowan
75056cfff5 version: bump version number 2025-09-19 14:18:36 -06:00
James Hilliard
7fbcb0dbd2 Fix race condition in BOSer multicommand causing CancelledError
The multicommand method was double-awaiting tasks - first via
asyncio.gather() with return_exceptions=True, then trying to await
the same tasks again. This caused CancelledError when gRPC connections
were lost.

Changed to properly use the results from gather() instead of
re-awaiting completed tasks, preventing the race condition and
properly handling exceptions.

Fixes StreamTerminatedError occurring in pyasic/web/braiins_os/boser.py:91
2025-09-19 14:17:59 -06:00
Brett Rowan
7329aeace2 version: bump version number 2025-09-17 19:18:51 -06:00
Brett Rowan
e8c3953106 bug: fix btminer V3 password 2025-09-17 19:18:27 -06:00
James Hilliard
a1a7562bdb Handle invalid unicode in json response 2025-09-17 19:11:31 -06:00
Brett Rowan
b2726c77a0 version: bump version number 2025-09-09 08:13:09 -06:00
Ryan Heideman
68299fa54d Avalon Nano3s Fixes (#374)
* Avalon Nano3s Fixes

* Fix to handle both cases

* [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-08-30 22:04:58 -06:00
UpstreamData
1466039e08 version: bump version number 2025-08-28 21:43:00 -06:00
Ryan Heideman
0aa72c6c85 Fix Byte Folder Names (#376) 2025-08-26 21:18:57 -06:00
Ryan Heideman
24134a5991 Goldshell Mini Doge Support (#375)
* Docs for Mini Doge

* Mini Doge Recognition Support

* Loading fixes

* Fix for number of fans

* Fixed expected_hashrate

* Fixes for hashboards

* Implemented uptime

* Fixed uptime

* Doc fixes

* Fixes for byte

* Copyright update

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

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

* Typo fix

* File renames

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-26 21:06:10 -06:00
Brett Rowan
038208efa6 version: bump version number 2025-08-16 10:25:55 -06:00
Brett Rowan
7e319b79df feature: add misidentified L7_i 2025-08-16 10:25:40 -06:00
Brett Rowan
a0fdec3ce5 version: bump version number 2025-08-16 10:06:58 -06:00
Brett Rowan
1a8928de18 feature: add retries to elphapex 2025-08-16 10:06:46 -06:00
Brett Rowan
bce0058930 version: bump version number 2025-08-16 09:38:24 -06:00
Brett Rowan
850656fce4 bug: fix elphapex hashboard parsing 2025-08-16 09:38:06 -06:00
Brett Rowan
8bb35d6d7c version: bump version number 2025-08-16 09:24:17 -06:00
Brett Rowan
e85f06dbc2 feature: add misidentified L9_i 2025-08-16 09:23:12 -06:00
Brett Rowan
a566801316 feature: add chip count for IceRiver KS5M 2025-08-16 09:16:07 -06:00
Brett Rowan
e1a9cc5d19 bug: fix some issues with whatsminer BTMinerV3 implementation 2025-08-16 09:14:11 -06:00
Brett Rowan
27bb06de2b version: bump version number 2025-08-15 14:36:04 -06:00
Brett Rowan
debd4d2d4d feature: add BTMiner V3 configuration 2025-08-15 14:35:32 -06:00
Brett Rowan
56ad6cbc6f feature: add dynamic version selection for whatsminers 2025-08-15 14:35:32 -06:00
Brett Rowan
3fa54213bf bug: handle vnish S19 pro hydro alternate naming 2025-08-15 14:35:15 -06:00
Brett Rowan
076958ec0e version: bump version number 2025-08-15 12:39:04 -06:00
Brett Rowan
5319089ebe feature: fix S21+ hydro for cases where there is no . at the end 2025-08-15 12:38:17 -06:00
Brett Rowan
76a77b51e8 version: bump version number 2025-08-14 13:25:26 -06:00
Brett Rowan
b099ff45d2 bug: remove print statements 2025-08-14 13:25:05 -06:00
Brett Rowan
9bc3cc221a version: bump version number 2025-08-14 11:41:50 -06:00
Brett Rowan
6418c2e102 feature: add BTMinerV3 control support 2025-08-14 11:41:08 -06:00
Brett Rowan
aa9f3b2c45 feature: add BTMinerV3 data support 2025-08-14 11:41:08 -06:00
Ryan Heideman
bb1c98f061 Goldshell Byte Support (#366) 2025-08-12 21:53:28 -06:00
Brett Rowan
d984431fe5 version: bump version number 2025-08-08 11:46:46 -06:00
Tony Scelfo
f1e4feb91e add expected chips for BraiinsModels BM100 and BM101 2025-08-08 11:45:43 -06:00
Brett Rowan
90c8986900 version: bump version number 2025-07-23 11:56:21 -06:00
Brett Rowan
5457ae6cd5 feature: add support for avalon Q 2025-07-23 11:55:57 -06:00
Brett Rowan
aa3d105fcb version: bump version number 2025-07-21 15:41:35 -06:00
Brett Rowan
77f59f6db6 feature: add support for Elphapex DG1 Home
Fixes: #311
2025-07-21 10:08:11 -06:00
Brett Rowan
3fa0d96fbb feature: add support for Iceriver AL3
Fixes: #317
2025-07-21 10:02:06 -06:00
Brett Rowan
e55477a8b8 feature: add support for S19j+
Fixes: #330
2025-07-21 09:51:51 -06:00
Brett Rowan
7d5744ae28 feature: add support for vnish XP Hydro
Fixes: #342
2025-07-21 09:48:11 -06:00
Brett Rowan
d4500be10c feature: add support for a couple alternate models
- Antminer S19j Pro+, ANTMINER
 - Antminer S19 Pro A, VNISH
 - Antminer S19 XP, VNISH
 - Antminer L9, VNISH
 - DG1, ELPHAPEX

 Fixes: #354
2025-07-21 09:35:00 -06:00
Brett Rowan
7ef2540133 feature: add support for Vnish S19 Hydro
Fixes: #355
2025-07-21 09:18:20 -06:00
Brett Rowan
1ea4f4e124 version: bump version number 2025-07-15 08:11:34 -06:00
bbemoll
a8a0e4a5fe Added support for Antminer S19XH Hydro running on Braiins OS (#353)
* Add pyasic files

* Add pyasic files

* Update pyproject.toml

* Update pyproject.toml

* Added support for Antminer S19XP Hydro running on Braiins OS

* [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-07-15 08:10:59 -06:00
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
229 changed files with 10713 additions and 3586 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ pyvenv.cfg
bin/
lib/
.idea/
.vs/

View File

@@ -1,9 +1,17 @@
ci:
skip:
- poetry-lock
- unittest
- generate-docs
repos:
- repo: https://github.com/python-poetry/poetry
rev: 2.2.1
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-install
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-yaml
@@ -14,22 +22,36 @@ repos:
name: check-yaml for other YAML files
exclude: ^mkdocs\.yml$
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.10.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.4
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.13.2
- id: ruff-check
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.18.2
hooks:
- id: isort
name: isort (python)
- id: mypy
additional_dependencies:
[
betterproto==2.0.0b7,
httpx==0.28.1,
types-aiofiles==24.1.0.20250822,
types-passlib==1.7.7.20250602,
pydantic==2.11.9,
]
- repo: local
hooks:
- id: unittest
name: unittest
entry: python -m unittest discover
language: system
'types': [python]
types: [ python ]
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
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
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

View File

@@ -1,7 +1,8 @@
import asyncio
import importlib
import os
import warnings
from pathlib import Path
from typing import Any
from pyasic.miners.factory import MINER_CLASSES, MinerTypes
@@ -51,12 +52,19 @@ def backend_str(backend: MinerTypes) -> str:
return "Mara Firmware Miners"
case MinerTypes.BITAXE:
return "Stock Firmware BitAxe Miners"
case MinerTypes.LUCKYMINER:
return "Stock Firmware Lucky Miners"
case MinerTypes.ICERIVER:
return "Stock Firmware IceRiver Miners"
case MinerTypes.HAMMER:
return "Stock Firmware Hammer Miners"
case MinerTypes.VOLCMINER:
return "Stock Firmware Volcminers"
case MinerTypes.ELPHAPEX:
return "Stock Firmware Elphapex Miners"
case MinerTypes.MSKMINER:
return "MSKMiner Firmware Miners"
raise TypeError("Unknown miner backend, cannot generate docs")
def create_url_str(mtype: str):
@@ -120,7 +128,7 @@ BACKEND_TYPE_CLOSER = """
</ul>
</details>"""
m_data = {}
m_data: dict[str, dict[str, list[type[Any]]]] = {}
done = []
for m in MINER_CLASSES:
@@ -135,14 +143,14 @@ for m in MINER_CLASSES:
done.append(miner)
async def create_directory_structure(directory, data):
def create_directory_structure(directory, data):
if not os.path.exists(directory):
os.makedirs(directory)
for key, value in data.items():
subdirectory = os.path.join(directory, key)
if isinstance(value, dict):
await create_directory_structure(subdirectory, value)
create_directory_structure(subdirectory, value)
elif isinstance(value, list):
file_path = os.path.join(subdirectory + ".md")
@@ -163,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:
file.write(SUPPORTED_TYPES_HEADER)
for mback in MINER_CLASSES:
@@ -180,7 +188,7 @@ async def create_supported_types(directory):
for mtype in backend_types:
file.write(MINER_TYPE_HEADER.format(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(
MINER_DETAILS.format(
make(minstance), mtype, create_url_str(model), model
@@ -190,6 +198,7 @@ async def create_supported_types(directory):
file.write(BACKEND_TYPE_CLOSER)
root_directory = os.path.join(os.getcwd(), "miners")
asyncio.run(create_directory_structure(root_directory, m_data))
asyncio.run(create_supported_types(root_directory))
if __name__ == "__main__":
root_directory = Path(__file__).parent.joinpath("miners")
create_directory_structure(root_directory, m_data)
create_supported_types(root_directory)

View File

@@ -157,6 +157,19 @@
show_root_heading: false
heading_level: 0
## S19j Pro+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j XP (Stock)
- [x] Shutdowns
@@ -170,6 +183,19 @@
show_root_heading: false
heading_level: 0
## S19j+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j No PIC (Stock)
- [x] Shutdowns
@@ -287,6 +313,19 @@
show_root_heading: false
heading_level: 0
## S19 XP Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XPHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (BOS+)
- [x] Shutdowns
@@ -443,6 +482,19 @@
show_root_heading: false
heading_level: 0
## S19 Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro (VNish)
- [x] Shutdowns
@@ -456,6 +508,19 @@
show_root_heading: false
heading_level: 0
## S19 Pro A (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProA
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro Hydro (VNish)
- [x] Shutdowns
@@ -469,6 +534,32 @@
show_root_heading: false
heading_level: 0
## S19 XP (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 XP Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19XPHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (VNish)
- [x] Shutdowns
@@ -495,6 +586,19 @@
show_root_heading: false
heading_level: 0
## S19i (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (VNish)
- [x] Shutdowns
@@ -664,6 +768,19 @@
show_root_heading: false
heading_level: 0
## S19k Pro Dual (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19kProDual
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 (Hive)
- [ ] Shutdowns
@@ -690,11 +807,50 @@
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)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19
@@ -707,7 +863,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19Pro
@@ -720,7 +876,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19XP
@@ -733,7 +889,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19jPro
@@ -746,7 +902,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19jProPlus
@@ -759,7 +915,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19kPro
@@ -772,7 +928,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.T19.LUXMinerT19

View File

@@ -14,6 +14,19 @@
show_root_heading: false
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)
- [x] Shutdowns
@@ -27,6 +40,32 @@
show_root_heading: false
heading_level: 0
## S21+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## 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)
- [x] Shutdowns
@@ -53,6 +92,58 @@
show_root_heading: false
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+)
- [x] Shutdowns
@@ -79,6 +170,58 @@
show_root_heading: false
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
@@ -135,7 +278,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X21.S21.LUXMinerS21
@@ -144,6 +287,19 @@
show_root_heading: false
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)
- [ ] Shutdowns

View File

@@ -73,7 +73,7 @@
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
::: pyasic.miners.antminer.vnish.X3.L3.VNishL3Plus
handler: python
options:
show_root_heading: false

View File

@@ -47,7 +47,7 @@
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
::: pyasic.miners.antminer.vnish.X7.L7.VNishL7
handler: python
options:
show_root_heading: false

View File

@@ -105,6 +105,19 @@
show_root_heading: false
heading_level: 0
## L9 (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X9.L9.VNishL9
handler: python
options:
show_root_heading: false
heading_level: 0
## T9 (Hive)
- [ ] Shutdowns
@@ -122,7 +135,7 @@
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Setpoints
- [x] Presets
::: 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

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

View File

@@ -1,6 +1,19 @@
# pyasic
## 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)
- [ ] Shutdowns

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
# pyasic
## byte Models
## Byte (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.goldshell.bfgminer.byte.byte.GoldshellByte
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,16 @@
# pyasic
## Mini Doge Models
## Mini Doge (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.goldshell.bfgminer.MiniDoge.MiniDoge.GoldshellMiniDoge
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,16 @@
# pyasic
## mini_doge Models
## Mini Doge (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.goldshell.bfgminer.mini_doge.mini_doge.GoldshellMiniDoge
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,16 @@
# pyasic
## ALX Models
## AL3 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.iceriver.iceminer.ALX.AL3.IceRiverAL3
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

@@ -38,6 +38,7 @@ details {
<details>
<summary>X7 Series:</summary>
<ul>
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
<li><a href="../antminer/X7#k7-stock">K7 (Stock)</a></li>
<li><a href="../antminer/X7#d7-stock">D7 (Stock)</a></li>
@@ -53,6 +54,7 @@ details {
<li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li>
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
<li><a href="../antminer/X9#l9-stock">L9 (Stock)</a></li>
<li><a href="../antminer/X9#l9-stock">L9 (Stock)</a></li>
</ul>
</details>
<details>
@@ -86,6 +88,8 @@ details {
<li><a href="../antminer/X19#s19j-no-pic-stock">S19j No PIC (Stock)</a></li>
<li><a href="../antminer/X19#s19-pro_1-stock">S19 Pro+ (Stock)</a></li>
<li><a href="../antminer/X19#s19j-pro-stock">S19j Pro (Stock)</a></li>
<li><a href="../antminer/X19#s19j_1-stock">S19j+ (Stock)</a></li>
<li><a href="../antminer/X19#s19j-pro_1-stock">S19j Pro+ (Stock)</a></li>
<li><a href="../antminer/X19#s19-xp-stock">S19 XP (Stock)</a></li>
<li><a href="../antminer/X19#s19a-stock">S19a (Stock)</a></li>
<li><a href="../antminer/X19#s19a-pro-stock">S19a Pro (Stock)</a></li>
@@ -103,8 +107,12 @@ details {
<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_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#t21-stock">T21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-hydro-stock">S21 Hydro (Stock)</a></li>
</ul>
</details>
</ul>
@@ -551,6 +559,19 @@ details {
<summary>nano Series:</summary>
<ul>
<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>
</details>
<details>
<summary>Q Series:</summary>
<ul>
<li><a href="../avalonminer/Q#avalon-q-home-stock">Avalon Q Home (Stock)</a></li>
</ul>
</details>
</ul>
@@ -603,6 +624,18 @@ details {
<li><a href="../goldshell/XBox#kd-box-pro-stock">KD Box Pro (Stock)</a></li>
</ul>
</details>
<details>
<summary>byte Series:</summary>
<ul>
<li><a href="../goldshell/byte#byte-stock">Byte (Stock)</a></li>
</ul>
</details>
<details>
<summary>mini_doge Series:</summary>
<ul>
<li><a href="../goldshell/mini_doge#mini-doge-stock">Mini Doge (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -646,12 +679,17 @@ details {
<li><a href="../antminer/X19#s19-xp-bos_1">S19 XP (BOS+)</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro-bos_1">S19 Pro+ Hydro (BOS+)</a></li>
<li><a href="../antminer/X19#t19-bos_1">T19 (BOS+)</a></li>
<li><a href="../antminer/X19#s19-xp-hydro-bos_1">S19 XP Hydro (BOS+)</a></li>
</ul>
</details>
<details>
<summary>X21 Series:</summary>
<ul>
<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>
</ul>
</details>
@@ -680,6 +718,12 @@ details {
<li><a href="../antminer/X7#l7-vnish">L7 (VNish)</a></li>
</ul>
</details>
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="../antminer/X9#l9-vnish">L9 (VNish)</a></li>
</ul>
</details>
<details>
<summary>X17 Series:</summary>
<ul>
@@ -694,11 +738,17 @@ details {
<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#s19j-vnish">S19j (VNish)</a></li>
<li><a href="../antminer/X19#s19i-vnish">S19i (VNish)</a></li>
<li><a href="../antminer/X19#s19-xp-vnish">S19 XP (VNish)</a></li>
<li><a href="../antminer/X19#s19-xp-hydro-vnish">S19 XP Hydro (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#s19-hydro-vnish">S19 Hydro (VNish)</a></li>
<li><a href="../antminer/X19#s19a-pro-vnish">S19a Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-a-vnish">S19 Pro A (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-hydro-vnish">S19 Pro Hydro (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-hydro-vnish">S19 Pro Hydro (VNish)</a></li>
<li><a href="../antminer/X19#s19k-pro-vnish">S19k Pro (VNish)</a></li>
<li><a href="../antminer/X19#t19-vnish">T19 (VNish)</a></li>
@@ -709,6 +759,10 @@ details {
<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_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>
</details>
</ul>
@@ -727,6 +781,7 @@ details {
<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#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>
</details>
<details>
@@ -742,6 +797,7 @@ details {
<ul>
<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-elite-1.0-epic">BlockMiner eLITE 1.0 (ePIC)</a></li>
</ul>
</details>
</ul>
@@ -760,6 +816,19 @@ details {
<ul>
<li><a href="../antminer/X19#s19j-pro-hive">S19j Pro (Hive)</a></li>
<li><a href="../antminer/X19#s19-hive">S19 (Hive)</a></li>
<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>
</details>
</ul>
@@ -789,6 +858,7 @@ details {
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-luxos">S21 (LuxOS)</a></li>
<li><a href="../antminer/X21#t21-luxos">T21 (LuxOS)</a></li>
</ul>
</details>
</ul>
@@ -859,6 +929,18 @@ details {
</ul>
</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>
<ul>
<details>
@@ -875,6 +957,12 @@ details {
<li><a href="../iceriver/KSX#ks5m-stock">KS5M (Stock)</a></li>
</ul>
</details>
<details>
<summary>ALX Series:</summary>
<ul>
<li><a href="../iceriver/ALX#al3-stock">AL3 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -898,4 +986,17 @@ details {
</ul>
</details>
</ul>
</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>
<li><a href="../elphapex/DGX#dg1-stock">DG1 (Stock)</a></li>
<li><a href="../elphapex/DGX#dg1home-stock">DG1Home (Stock)</a></li>
</ul>
</details>
</ul>
</details>

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK10
@@ -18,7 +18,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK20
@@ -31,7 +31,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK30
@@ -44,7 +44,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK40
@@ -57,7 +57,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK50
@@ -70,7 +70,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK60
@@ -83,7 +83,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVL20
@@ -96,7 +96,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVL30
@@ -109,7 +109,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVL40
@@ -122,7 +122,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVL50
@@ -135,7 +135,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVL60
@@ -148,7 +148,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH30
@@ -161,7 +161,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH40
@@ -174,7 +174,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ30
@@ -187,7 +187,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ40
@@ -200,7 +200,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ60
@@ -213,7 +213,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK10
@@ -226,7 +226,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK20
@@ -239,7 +239,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK30
@@ -252,7 +252,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVL10
@@ -265,7 +265,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVL20
@@ -278,7 +278,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVL30
@@ -291,7 +291,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH10
@@ -304,7 +304,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH20
@@ -317,7 +317,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH30
@@ -330,7 +330,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH40
@@ -343,7 +343,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH50
@@ -356,7 +356,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ10
@@ -369,7 +369,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ20
@@ -382,7 +382,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ30
@@ -395,7 +395,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ40
@@ -408,7 +408,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ50
@@ -421,7 +421,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK10
@@ -434,7 +434,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK20
@@ -447,7 +447,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK30
@@ -460,7 +460,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK50
@@ -473,7 +473,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK60
@@ -486,7 +486,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK70
@@ -499,7 +499,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVK80
@@ -512,7 +512,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVL20
@@ -525,7 +525,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVL30
@@ -538,7 +538,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30
@@ -551,7 +551,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30
@@ -564,7 +564,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH10
@@ -577,7 +577,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH20
@@ -590,7 +590,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH30
@@ -603,7 +603,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH40
@@ -616,7 +616,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH50
@@ -629,7 +629,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH60
@@ -642,7 +642,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH70
@@ -655,7 +655,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH80
@@ -668,7 +668,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH90
@@ -681,7 +681,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
@@ -694,7 +694,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ20
@@ -707,7 +707,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ30
@@ -720,7 +720,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ40
@@ -733,7 +733,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ60
@@ -746,7 +746,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VK40
@@ -759,7 +759,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VK50
@@ -772,7 +772,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M52S_Plus_Plus.BTMinerM52SPlusPlusVL10
@@ -785,7 +785,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M52S.BTMinerM52SVK30
@@ -798,7 +798,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53H.BTMinerM53HVH10
@@ -811,7 +811,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK10
@@ -824,7 +824,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK20
@@ -837,7 +837,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK30
@@ -850,7 +850,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK50
@@ -863,7 +863,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVL10
@@ -876,7 +876,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVL30
@@ -889,7 +889,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30
@@ -902,7 +902,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ40
@@ -915,7 +915,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ50
@@ -928,7 +928,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVK30
@@ -941,7 +941,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH20
@@ -954,7 +954,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH30
@@ -967,7 +967,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ30
@@ -980,7 +980,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ40
@@ -993,7 +993,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVK30
@@ -1006,7 +1006,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH30
@@ -1019,7 +1019,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH40
@@ -1032,7 +1032,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH50
@@ -1045,7 +1045,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VK30
@@ -1058,7 +1058,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VK60
@@ -1071,7 +1071,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M54S_Plus_Plus.BTMinerM54SPlusPlusVK30
@@ -1084,7 +1084,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M54S_Plus_Plus.BTMinerM54SPlusPlusVL30
@@ -1097,7 +1097,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M54S_Plus_Plus.BTMinerM54SPlusPlusVL40
@@ -1110,7 +1110,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus_Plus.BTMinerM56SPlusPlusVK10
@@ -1123,7 +1123,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus_Plus.BTMinerM56SPlusPlusVK30
@@ -1136,7 +1136,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus_Plus.BTMinerM56SPlusPlusVK40
@@ -1149,7 +1149,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus_Plus.BTMinerM56SPlusPlusVK50
@@ -1162,7 +1162,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVJ30
@@ -1175,7 +1175,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVK30
@@ -1188,7 +1188,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVK40
@@ -1201,7 +1201,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVK50
@@ -1214,7 +1214,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVH30
@@ -1227,7 +1227,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVJ30
@@ -1240,7 +1240,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVJ40
@@ -1253,7 +1253,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30
@@ -1266,7 +1266,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M5X.M59.BTMinerM59VH30

View File

@@ -5,7 +5,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus_Plus.BTMinerM60SPlusPlusVL30
@@ -18,7 +18,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus_Plus.BTMinerM60SPlusPlusVL40
@@ -31,7 +31,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVK30
@@ -44,7 +44,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVK40
@@ -57,7 +57,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVK50
@@ -70,7 +70,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVK60
@@ -83,7 +83,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVK70
@@ -96,7 +96,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVL10
@@ -109,7 +109,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVL30
@@ -122,7 +122,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVL40
@@ -135,7 +135,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVL50
@@ -148,7 +148,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S_Plus.BTMinerM60SPlusVL60
@@ -161,7 +161,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK10
@@ -174,7 +174,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK20
@@ -187,7 +187,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK30
@@ -200,7 +200,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK40
@@ -213,7 +213,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL10
@@ -226,7 +226,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL20
@@ -239,7 +239,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL30
@@ -252,7 +252,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL40
@@ -265,7 +265,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL50
@@ -278,7 +278,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL60
@@ -291,7 +291,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVL70
@@ -304,7 +304,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK10
@@ -317,7 +317,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK20
@@ -330,7 +330,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK30
@@ -343,7 +343,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK40
@@ -356,7 +356,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK6A
@@ -369,7 +369,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VL10
@@ -382,7 +382,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VL20
@@ -395,7 +395,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VL30
@@ -408,7 +408,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VL40
@@ -421,7 +421,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VL50
@@ -434,7 +434,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61S_Plus.BTMinerM61SPlusVL30
@@ -447,7 +447,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61S.BTMinerM61SVL10
@@ -460,7 +460,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61S.BTMinerM61SVL20
@@ -473,7 +473,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61S.BTMinerM61SVL30
@@ -486,7 +486,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VK10
@@ -499,7 +499,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VK20
@@ -512,7 +512,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VK30
@@ -525,7 +525,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VK40
@@ -538,7 +538,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VL10
@@ -551,7 +551,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VL30
@@ -564,7 +564,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VL40
@@ -577,7 +577,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VL50
@@ -590,7 +590,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M61.BTMinerM61VL60
@@ -603,7 +603,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M62S_Plus.BTMinerM62SPlusVK30
@@ -616,7 +616,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S_Plus_Plus.BTMinerM63SPlusPlusVL20
@@ -629,7 +629,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S_Plus.BTMinerM63SPlusVK30
@@ -642,7 +642,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S_Plus.BTMinerM63SPlusVL10
@@ -655,7 +655,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S_Plus.BTMinerM63SPlusVL20
@@ -668,7 +668,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S_Plus.BTMinerM63SPlusVL30
@@ -681,7 +681,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S_Plus.BTMinerM63SPlusVL50
@@ -694,7 +694,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK10
@@ -707,7 +707,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK20
@@ -720,7 +720,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK30
@@ -733,7 +733,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK60
@@ -746,7 +746,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVL10
@@ -759,7 +759,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVL50
@@ -772,7 +772,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVL60
@@ -785,7 +785,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK10
@@ -798,7 +798,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK20
@@ -811,7 +811,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK30
@@ -824,7 +824,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VL10
@@ -837,7 +837,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VL30
@@ -850,7 +850,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M64S.BTMinerM64SVL30
@@ -863,7 +863,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M64.BTMinerM64VL30
@@ -876,7 +876,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M64.BTMinerM64VL40
@@ -889,7 +889,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M65S_Plus.BTMinerM65SPlusVK30
@@ -902,7 +902,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M65S.BTMinerM65SVK20
@@ -915,7 +915,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M65S.BTMinerM65SVL60
@@ -928,7 +928,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus_Plus.BTMinerM66SPlusPlusVL20
@@ -941,7 +941,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus.BTMinerM66SPlusVK30
@@ -954,7 +954,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus.BTMinerM66SPlusVL10
@@ -967,7 +967,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus.BTMinerM66SPlusVL20
@@ -980,7 +980,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus.BTMinerM66SPlusVL30
@@ -993,7 +993,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus.BTMinerM66SPlusVL40
@@ -1006,7 +1006,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S_Plus.BTMinerM66SPlusVL60
@@ -1019,7 +1019,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK20
@@ -1032,7 +1032,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK30
@@ -1045,7 +1045,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK40
@@ -1058,7 +1058,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK50
@@ -1071,7 +1071,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK60
@@ -1084,7 +1084,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVL10
@@ -1097,7 +1097,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVL20
@@ -1110,7 +1110,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVL30
@@ -1123,7 +1123,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVL40
@@ -1136,7 +1136,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVL50
@@ -1149,7 +1149,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK20
@@ -1162,7 +1162,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK30
@@ -1175,7 +1175,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VL20
@@ -1188,7 +1188,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VL30
@@ -1201,7 +1201,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M6X.M67S.BTMinerM67SVK30

View File

@@ -5,7 +5,7 @@
- [x] Shutdowns
- [x] Power Modes
- [x] Setpoints
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.whatsminer.btminer.M7X.M70.BTMinerM70VM30

View File

@@ -97,6 +97,8 @@ nav:
- Innosilicon T3X: "miners/innosilicon/T3X.md"
- Innosilicon A10X: "miners/innosilicon/A10X.md"
- Innosilicon A11X: "miners/innosilicon/A11X.md"
- Goldshell Byte: "miners/goldshell/Byte.md"
- Goldshell Mini Doge: "miners/goldshell/MiniDoge.md"
- Goldshell X5: "miners/goldshell/X5.md"
- Goldshell XMax: "miners/goldshell/XMax.md"
- Goldshell XBox: "miners/goldshell/XBox.md"

1307
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 -
# limitations under the License. -
# ------------------------------------------------------------------------------
import importlib.metadata
from pyasic import settings
from pyasic.config import MinerConfig
from pyasic.data import MinerData
@@ -22,3 +24,5 @@ from pyasic.network import MinerNetwork
from pyasic.rpc import *
from pyasic.ssh import *
from pyasic.web import *
__version__ = importlib.metadata.version("pyasic")

View File

@@ -14,10 +14,41 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import Any
from pydantic import BaseModel, Field
from pyasic.config.fans import FanMode, FanModeConfig, FanModeNormal
from pyasic.config.mining import MiningMode, MiningModeConfig
from pyasic.config.fans import (
FanModeConfig,
FanModeImmersion,
FanModeManual,
FanModeNormal,
)
from pyasic.config.mining import (
MiningModeConfig,
MiningModeHashrateTune,
MiningModeHPM,
MiningModeLPM,
MiningModeManual,
MiningModeNormal,
MiningModePowerTune,
MiningModePreset,
MiningModeSleep,
)
# Type aliases for config field types
FanModeType = FanModeNormal | FanModeManual | FanModeImmersion | FanModeConfig
MiningModeType = (
MiningModeNormal
| MiningModeHPM
| MiningModeLPM
| MiningModeSleep
| MiningModeManual
| MiningModePowerTune
| MiningModeHashrateTune
| MiningModePreset
| MiningModeConfig
)
from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig
from pyasic.config.temperature import TemperatureConfig
@@ -32,11 +63,11 @@ class MinerConfig(BaseModel):
arbitrary_types_allowed = True
pools: PoolConfig = Field(default_factory=PoolConfig.default)
fan_mode: FanMode = Field(default_factory=FanModeConfig.default)
fan_mode: FanModeType = Field(default_factory=FanModeConfig.default)
temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default)
mining_mode: MiningMode = Field(default_factory=MiningModeConfig.default)
mining_mode: MiningModeType = Field(default_factory=MiningModeConfig.default)
def __getitem__(self, item):
def __getitem__(self, item: str) -> Any:
try:
return getattr(self, item)
except AttributeError:
@@ -56,6 +87,26 @@ class MinerConfig(BaseModel):
**self.temperature.as_am_modern(),
}
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."""
return {
@@ -65,6 +116,13 @@ class MinerConfig(BaseModel):
**self.temperature.as_wm(),
}
def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Whatsminers running BTMiner V3."""
return {
"set.miner.pools": self.pools.as_btminer_v3(),
**self.mining_mode.as_btminer_v3(),
}
def as_am_old(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for old versions of Antminers."""
return {
@@ -143,12 +201,12 @@ class MinerConfig(BaseModel):
**self.pools.as_mara(user_suffix=user_suffix),
}
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
def as_espminer(self, user_suffix: str | None = None) -> dict:
return {
**self.fan_mode.as_bitaxe(),
**self.temperature.as_bitaxe(),
**self.mining_mode.as_bitaxe(),
**self.pools.as_bitaxe(user_suffix=user_suffix),
**self.fan_mode.as_espminer(),
**self.temperature.as_espminer(),
**self.mining_mode.as_espminer(),
**self.pools.as_espminer(user_suffix=user_suffix),
}
def as_luxos(self, user_suffix: str | None = None) -> dict:
@@ -199,6 +257,24 @@ class MinerConfig(BaseModel):
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
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
@@ -209,6 +285,16 @@ class MinerConfig(BaseModel):
"""Constructs a MinerConfig object from web configuration for Goldshell miners."""
return cls(pools=PoolConfig.from_am_modern(web_conf))
@classmethod
def from_goldshell_list(cls, web_conf: list) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for Goldshell miners."""
return cls(pools=PoolConfig.from_goldshell(web_conf))
@classmethod
def from_goldshell_byte(cls, web_conf: list) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for Goldshell Byte miners."""
return cls(pools=PoolConfig.from_goldshell_byte(web_conf))
@classmethod
def from_inno(cls, web_pools: list) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for Innosilicon miners."""
@@ -245,13 +331,17 @@ class MinerConfig(BaseModel):
)
@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."""
return cls(
pools=PoolConfig.from_vnish(web_settings),
fan_mode=FanModeConfig.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
@@ -272,10 +362,10 @@ class MinerConfig(BaseModel):
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig":
def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_bitaxe(web_system_info),
fan_mode=FanModeConfig.from_bitaxe(web_system_info),
pools=PoolConfig.from_espminer(web_system_info),
fan_mode=FanModeConfig.from_espminer(web_system_info),
)
@classmethod
@@ -310,5 +400,12 @@ class MinerConfig(BaseModel):
return cls.from_am_modern(*args, **kwargs)
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
return cls.from_am_modern(web_conf)
def from_btminer_v3(
cls, rpc_pools: dict, rpc_settings: dict, rpc_device_info: dict
) -> "MinerConfig":
return cls(
pools=PoolConfig.from_btminer_v3(rpc_pools=rpc_pools["msg"]),
mining_mode=MiningModeConfig.from_btminer_v3(
rpc_device_info=rpc_device_info, rpc_settings=rpc_settings
),
)

View File

@@ -16,6 +16,7 @@
from __future__ import annotations
from enum import Enum
from typing import Any
from pydantic import BaseModel
@@ -28,6 +29,9 @@ class MinerConfigOption(Enum):
def as_am_modern(self) -> dict:
return self.value.as_am_modern()
def as_hiveon_modern(self) -> dict:
return self.value.as_hiveon_modern()
def as_am_old(self) -> dict:
return self.value.as_am_old()
@@ -61,12 +65,15 @@ class MinerConfigOption(Enum):
def as_mara(self) -> dict:
return self.value.as_mara()
def as_bitaxe(self) -> dict:
return self.value.as_bitaxe()
def as_espminer(self) -> dict:
return self.value.as_espminer()
def as_luxos(self) -> dict:
return self.value.as_luxos()
def as_elphapex(self) -> dict:
return self.value.as_elphapex()
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
@@ -83,52 +90,61 @@ class MinerConfigOption(Enum):
class MinerConfigValue(BaseModel):
@classmethod
def from_dict(cls, dict_conf: dict | None):
def from_dict(cls, dict_conf: dict):
return cls()
def as_dict(self) -> dict:
return self.model_dump()
def as_am_modern(self) -> dict:
def as_am_modern(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_am_old(self) -> dict:
def as_hiveon_modern(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_wm(self) -> dict:
def as_am_old(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_inno(self) -> dict:
def as_wm(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_goldshell(self) -> dict:
def as_btminer_v3(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_avalon(self) -> dict:
def as_inno(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_bosminer(self) -> dict:
def as_goldshell(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_boser(self) -> dict:
def as_avalon(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_epic(self) -> dict:
def as_bosminer(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_vnish(self) -> dict:
def as_boser(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_auradine(self) -> dict:
def as_epic(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_mara(self) -> dict:
def as_vnish(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_bitaxe(self) -> dict:
def as_auradine(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_luxos(self) -> dict:
def as_mara(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_espminer(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_luxos(self, *args: Any, **kwargs: Any) -> Any:
return {}
def as_elphapex(self, *args: Any, **kwargs: Any) -> Any:
return {}
def __getitem__(self, item):

View File

@@ -15,7 +15,7 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from typing import TypeVar, Union
from typing import TypeVar
from pydantic import Field
@@ -28,7 +28,7 @@ class FanModeNormal(MinerConfigValue):
minimum_speed: int = 0
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "FanModeNormal":
def from_dict(cls, dict_conf: dict) -> FanModeNormal:
cls_conf = {}
if dict_conf.get("minimum_fans") is not None:
cls_conf["minimum_fans"] = dict_conf["minimum_fans"]
@@ -37,7 +37,7 @@ class FanModeNormal(MinerConfigValue):
return cls(**cls_conf)
@classmethod
def from_vnish(cls, web_cooling_settings: dict) -> "FanModeNormal":
def from_vnish(cls, web_cooling_settings: dict) -> FanModeNormal:
cls_conf = {}
if web_cooling_settings.get("fan_min_count") is not None:
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
@@ -55,6 +55,12 @@ class FanModeNormal(MinerConfigValue):
def as_am_modern(self) -> dict:
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:
return {
"temp_control": {"mode": "auto"},
@@ -81,7 +87,7 @@ class FanModeNormal(MinerConfigValue):
},
}
def as_bitaxe(self) -> dict:
def as_espminer(self) -> dict:
return {"autoFanspeed": 1}
def as_luxos(self) -> dict:
@@ -106,7 +112,7 @@ class FanModeManual(MinerConfigValue):
minimum_fans: int = 1
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "FanModeManual":
def from_dict(cls, dict_conf: dict) -> FanModeManual:
cls_conf = {}
if dict_conf.get("speed") is not None:
cls_conf["speed"] = dict_conf["speed"]
@@ -115,7 +121,7 @@ class FanModeManual(MinerConfigValue):
return cls(**cls_conf)
@classmethod
def from_bosminer(cls, toml_fan_conf: dict) -> "FanModeManual":
def from_bosminer(cls, toml_fan_conf: dict) -> FanModeManual:
cls_conf = {}
if toml_fan_conf.get("min_fans") is not None:
cls_conf["minimum_fans"] = toml_fan_conf["min_fans"]
@@ -124,7 +130,7 @@ class FanModeManual(MinerConfigValue):
return cls(**cls_conf)
@classmethod
def from_vnish(cls, web_cooling_settings: dict) -> "FanModeManual":
def from_vnish(cls, web_cooling_settings: dict) -> FanModeManual:
cls_conf = {}
if web_cooling_settings.get("fan_min_count") is not None:
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
@@ -135,6 +141,12 @@ class FanModeManual(MinerConfigValue):
def as_am_modern(self) -> dict:
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:
return {
"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}
def as_luxos(self) -> dict:
@@ -179,12 +191,18 @@ class FanModeImmersion(MinerConfigValue):
mode: str = Field(init=False, default="immersion")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "FanModeImmersion":
def from_dict(cls, dict_conf: dict | None) -> FanModeImmersion:
return cls()
def as_am_modern(self) -> dict:
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:
return {
"fan_control": {"min_fans": 0},
@@ -239,6 +257,34 @@ class FanModeConfig(MinerConfigOption):
else:
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
def from_epic(cls, web_conf: dict):
try:
@@ -342,7 +388,7 @@ class FanModeConfig(MinerConfigOption):
return cls.default()
@classmethod
def from_bitaxe(cls, web_system_info: dict):
def from_espminer(cls, web_system_info: dict):
if web_system_info["autofanspeed"] == 1:
return cls.normal()
else:
@@ -371,5 +417,5 @@ class FanModeConfig(MinerConfigOption):
FanMode = TypeVar(
"FanMode",
bound=Union[FanModeNormal, FanModeManual, FanModeImmersion],
bound=FanModeNormal | FanModeManual | FanModeImmersion,
)

View File

@@ -16,7 +16,7 @@
from __future__ import annotations
from dataclasses import field
from typing import TypeVar, Union
from typing import Any, TypeVar
from pyasic import settings
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@@ -35,7 +35,14 @@ from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
TunerPerformanceMode,
)
from .algo import TunerAlgo, TunerAlgoType
from .algo import (
BoardTuneAlgo,
ChipTuneAlgo,
StandardTuneAlgo,
TunerAlgo,
TunerAlgoType,
VOptAlgo,
)
from .presets import MiningPreset
from .scaling import ScalingConfig
@@ -44,7 +51,7 @@ class MiningModeNormal(MinerConfigValue):
mode: str = field(init=False, default="normal")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeNormal":
def from_dict(cls, dict_conf: dict | None) -> MiningModeNormal:
return cls()
def as_am_modern(self) -> dict:
@@ -52,9 +59,20 @@ class MiningModeNormal(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict:
return {"mode": self.mode}
def as_btminer_v3(self) -> dict:
return {"set.miner.service": "start", "set.miner.power_mode": self.mode}
def as_auradine(self) -> dict:
return {"mode": {"mode": self.mode}}
@@ -74,12 +92,15 @@ class MiningModeNormal(MinerConfigValue):
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": False}}
def as_bosminer(self) -> dict:
return {"autotuning": {"enabled": True}}
class MiningModeSleep(MinerConfigValue):
mode: str = field(init=False, default="sleep")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeSleep":
def from_dict(cls, dict_conf: dict | None) -> MiningModeSleep:
return cls()
def as_am_modern(self) -> dict:
@@ -87,9 +108,20 @@ class MiningModeSleep(MinerConfigValue):
return {"miner-mode": "1"}
return {"miner-mode": 1}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "1"}
return {"miner-mode": 1}
def as_elphapex(self) -> dict:
return {"miner-mode": 1}
def as_wm(self) -> dict:
return {"mode": self.mode}
def as_btminer_v3(self) -> dict:
return {"set.miner.service": "stop"}
def as_auradine(self) -> dict:
return {"mode": {"sleep": "on"}}
@@ -111,7 +143,7 @@ class MiningModeLPM(MinerConfigValue):
mode: str = field(init=False, default="low")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeLPM":
def from_dict(cls, dict_conf: dict | None) -> MiningModeLPM:
return cls()
def as_am_modern(self) -> dict:
@@ -119,9 +151,20 @@ class MiningModeLPM(MinerConfigValue):
return {"miner-mode": "3"}
return {"miner-mode": 3}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "3"}
return {"miner-mode": 3}
def as_elphapex(self) -> dict:
return {"miner-mode": 3}
def as_wm(self) -> dict:
return {"mode": self.mode}
def as_btminer_v3(self) -> dict:
return {"set.miner.service": "start", "set.miner.power_mode": self.mode}
def as_auradine(self) -> dict:
return {"mode": {"mode": "eco"}}
@@ -133,7 +176,7 @@ class MiningModeHPM(MinerConfigValue):
mode: str = field(init=False, default="high")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHPM":
def from_dict(cls, dict_conf: dict | None) -> MiningModeHPM:
return cls()
def as_am_modern(self) -> dict:
@@ -141,9 +184,20 @@ class MiningModeHPM(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict:
return {"mode": self.mode}
def as_btminer_v3(self) -> dict:
return {"set.miner.service": "start", "set.miner.power_mode": self.mode}
def as_auradine(self) -> dict:
return {"mode": {"mode": "turbo"}}
@@ -154,11 +208,15 @@ class MiningModePowerTune(MinerConfigValue):
mode: str = field(init=False, default="power_tuning")
power: int | None = None
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
algo: StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo = field(
default_factory=TunerAlgo.default
)
scaling: ScalingConfig | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
def from_dict(cls, dict_conf: dict | None) -> MiningModePowerTune:
if dict_conf is None:
return cls()
cls_conf = {}
if dict_conf.get("power"):
cls_conf["power"] = dict_conf["power"]
@@ -174,11 +232,22 @@ class MiningModePowerTune(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict:
if self.power is not None:
return {"mode": self.mode, self.mode: {"wattage": self.power}}
return {}
def as_btminer_v3(self) -> dict:
return {"set.miner.service": "start", "set.miner.power_limit": self.power}
def as_bosminer(self) -> dict:
tuning_cfg = {"enabled": True, "mode": "power_target"}
if self.power is not None:
@@ -187,25 +256,27 @@ class MiningModePowerTune(MinerConfigValue):
cfg = {"autotuning": tuning_cfg}
if self.scaling is not None:
scaling_cfg = {"enabled": True}
scaling_cfg: dict[str, Any] = {"enabled": True}
if self.scaling.step is not None:
scaling_cfg["power_step"] = self.scaling.step
if self.scaling.minimum is not None:
scaling_cfg["min_power_target"] = self.scaling.minimum
if self.scaling.shutdown is not None:
scaling_cfg = {**scaling_cfg, **self.scaling.shutdown.as_bosminer()}
scaling_cfg.update(self.scaling.shutdown.as_bosminer())
cfg["performance_scaling"] = scaling_cfg
return cfg
def as_boser(self) -> dict:
cfg = {
cfg: dict[str, Any] = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_AND_APPLY,
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
power_target=PowerTargetMode(
power_target=Power(watt=self.power)
if self.power is not None
else None # type: ignore[arg-type]
)
)
),
@@ -215,18 +286,20 @@ class MiningModePowerTune(MinerConfigValue):
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
enable=True,
**sd_cfg,
target=DpsTarget(
power_target=DpsPowerTarget(
power_step=Power(self.scaling.step),
min_power_target=Power(self.scaling.minimum),
)
),
),
power_target_kwargs: dict[str, Any] = {}
if self.scaling.step is not None:
power_target_kwargs["power_step"] = Power(watt=self.scaling.step)
if self.scaling.minimum is not None:
power_target_kwargs["min_power_target"] = Power(
watt=self.scaling.minimum
)
cfg["set_dps"] = SetDpsRequest(
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
enable=True,
**sd_cfg,
target=DpsTarget(power_target=DpsPowerTarget(**power_target_kwargs)),
)
return cfg
def as_auradine(self) -> dict:
@@ -253,11 +326,15 @@ class MiningModeHashrateTune(MinerConfigValue):
mode: str = field(init=False, default="hashrate_tuning")
hashrate: int | None = None
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
algo: StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo = field(
default_factory=TunerAlgo.default
)
scaling: ScalingConfig | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
def from_dict(cls, dict_conf: dict | None) -> MiningModeHashrateTune:
if dict_conf is None:
return cls()
cls_conf = {}
if dict_conf.get("hashrate"):
cls_conf["hashrate"] = dict_conf["hashrate"]
@@ -273,22 +350,31 @@ class MiningModeHashrateTune(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_bosminer(self) -> dict:
conf = {"enabled": True, "mode": "hashrate_target"}
if self.hashrate is not None:
conf["hashrate_target"] = self.hashrate
return {"autotuning": conf}
@property
def as_boser(self) -> dict:
cfg = {
cfg: dict[str, Any] = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_AND_APPLY,
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
hashrate_target=HashrateTargetMode(
hashrate_target=TeraHashrate(
terahash_per_second=self.hashrate
terahash_per_second=float(self.hashrate)
if self.hashrate is not None
else None # type: ignore[arg-type]
)
)
)
@@ -299,18 +385,24 @@ class MiningModeHashrateTune(MinerConfigValue):
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
enable=True,
**sd_cfg,
target=DpsTarget(
hashrate_target=DpsHashrateTarget(
hashrate_step=TeraHashrate(self.scaling.step),
min_hashrate_target=TeraHashrate(self.scaling.minimum),
)
),
hashrate_target_kwargs: dict[str, Any] = {}
if self.scaling.step is not None:
hashrate_target_kwargs["hashrate_step"] = TeraHashrate(
terahash_per_second=float(self.scaling.step)
)
if self.scaling.minimum is not None:
hashrate_target_kwargs["min_hashrate_target"] = TeraHashrate(
terahash_per_second=float(self.scaling.minimum)
)
cfg["set_dps"] = SetDpsRequest(
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
enable=True,
**sd_cfg,
target=DpsTarget(
hashrate_target=DpsHashrateTarget(**hashrate_target_kwargs)
),
)
return cfg
def as_auradine(self) -> dict:
@@ -319,7 +411,11 @@ class MiningModeHashrateTune(MinerConfigValue):
def as_epic(self) -> dict:
mode = {
"ptune": {
"algo": self.algo.as_epic(),
"algo": (
self.algo.as_epic()
if hasattr(self.algo, "as_epic")
else TunerAlgo.default().as_epic()
),
"target": self.hashrate,
}
}
@@ -356,24 +452,28 @@ class MiningModePreset(MinerConfigValue):
@classmethod
def from_vnish(
cls, web_overclock_settings: dict, web_presets: list[dict]
) -> "MiningModePreset":
active_preset = None
for preset in web_presets:
if preset["name"] == web_overclock_settings["preset"]:
active_preset = preset
cls,
web_overclock_settings: dict,
web_presets: list[dict],
web_perf_summary: dict,
) -> MiningModePreset:
active_preset = web_perf_summary.get("current_preset")
if active_preset is None:
for preset in web_presets:
if preset["name"] == web_overclock_settings["preset"]:
active_preset = preset
return cls(
active_preset=MiningPreset.from_vnish(active_preset),
active_preset=MiningPreset.from_vnish(active_preset or {}),
available_presets=[MiningPreset.from_vnish(p) for p in web_presets],
)
@classmethod
def from_luxos(
cls, rpc_config: dict, rpc_profiles: list[dict]
) -> "MiningModePreset":
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModePreset:
active_preset = cls.get_active_preset_from_luxos(rpc_config, rpc_profiles)
return cls(
active_preset=MiningPreset.from_luxos(active_preset),
active_preset=active_preset,
available_presets=[
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
],
@@ -381,14 +481,14 @@ class MiningModePreset(MinerConfigValue):
@classmethod
def get_active_preset_from_luxos(
cls, rpc_config: dict, rpc_profiles: list[dict]
) -> dict:
cls, rpc_config: dict, rpc_profiles: dict
) -> MiningPreset:
active_preset = None
active_profile = rpc_config["CONFIG"][0]["Profile"]
for profile in rpc_profiles["PROFILES"]:
if profile["Profile Name"] == active_profile:
active_preset = profile
return active_preset
return MiningPreset.from_luxos(active_preset or {})
class ManualBoardSettings(MinerConfigValue):
@@ -396,7 +496,7 @@ class ManualBoardSettings(MinerConfigValue):
volt: float
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ManualBoardSettings":
def from_dict(cls, dict_conf: dict) -> ManualBoardSettings:
return cls(freq=dict_conf["freq"], volt=dict_conf["volt"])
def as_am_modern(self) -> dict:
@@ -404,6 +504,14 @@ class ManualBoardSettings(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {"freq": self.freq}
@@ -416,11 +524,15 @@ class MiningModeManual(MinerConfigValue):
boards: dict[int, ManualBoardSettings] = field(default_factory=dict)
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeManual":
def from_dict(cls, dict_conf: dict) -> MiningModeManual:
return cls(
global_freq=dict_conf["global_freq"],
global_volt=dict_conf["global_volt"],
boards={i: ManualBoardSettings.from_dict(dict_conf[i]) for i in dict_conf},
boards={
i: ManualBoardSettings.from_dict(dict_conf[i])
for i in dict_conf
if isinstance(i, int)
},
)
def as_am_modern(self) -> dict:
@@ -428,6 +540,9 @@ class MiningModeManual(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_vnish(self) -> dict:
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
return {
@@ -441,7 +556,7 @@ class MiningModeManual(MinerConfigValue):
}
@classmethod
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
def from_vnish(cls, web_overclock_settings: dict) -> MiningModeManual:
# will raise KeyError if it cant find the settings, values cannot be empty
voltage = web_overclock_settings["globals"]["volt"]
freq = web_overclock_settings["globals"]["freq"]
@@ -455,7 +570,7 @@ class MiningModeManual(MinerConfigValue):
return cls(global_freq=freq, global_volt=voltage, boards=boards)
@classmethod
def from_epic(cls, epic_conf: dict) -> "MiningModeManual":
def from_epic(cls, epic_conf: dict) -> MiningModeManual:
voltage = 0
freq = 0
if epic_conf.get("HwConfig") is not None:
@@ -495,11 +610,11 @@ class MiningModeConfig(MinerConfigOption):
manual = MiningModeManual
@classmethod
def default(cls):
def default(cls) -> MiningModeConfig:
return cls.normal()
@classmethod
def from_dict(cls, dict_conf: dict | None):
def from_dict(cls, dict_conf: dict | None) -> MiningModeConfig:
if dict_conf is None:
return cls.default()
@@ -507,12 +622,13 @@ class MiningModeConfig(MinerConfigOption):
if mode is None:
return cls.default()
cls_attr = getattr(cls, mode)
cls_attr = getattr(cls, mode, None)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)
return cls.default()
@classmethod
def from_am_modern(cls, web_conf: dict):
def from_am_modern(cls, web_conf: dict) -> MiningModeConfig:
if web_conf.get("bitmain-work-mode") is not None:
work_mode = web_conf["bitmain-work-mode"]
if work_mode == "":
@@ -526,7 +642,35 @@ class MiningModeConfig(MinerConfigOption):
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
def from_hiveon_modern(cls, web_conf: dict) -> MiningModeConfig:
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) -> MiningModeConfig:
if web_conf.get("fc-work-mode") is not None:
work_mode = web_conf["fc-work-mode"]
if work_mode == "":
return cls.default()
if int(work_mode) == 0:
return cls.normal()
elif int(work_mode) == 1:
return cls.sleep()
elif int(work_mode) == 3:
return cls.low()
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict) -> MiningModeConfig:
try:
tuner_running = web_conf["PerpetualTune"]["Running"]
if tuner_running:
@@ -565,12 +709,12 @@ class MiningModeConfig(MinerConfigOption):
algo=TunerAlgo.chip_tune(),
)
else:
return MiningModeManual.from_epic(web_conf)
return cls.manual.from_epic(web_conf)
except KeyError:
return cls.default()
@classmethod
def from_bosminer(cls, toml_conf: dict):
def from_bosminer(cls, toml_conf: dict) -> MiningModeConfig:
if toml_conf.get("autotuning") is None:
return cls.default()
autotuning_conf = toml_conf["autotuning"]
@@ -607,21 +751,24 @@ class MiningModeConfig(MinerConfigOption):
return cls.hashrate_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
return cls.default()
@classmethod
def from_vnish(cls, web_settings: dict, web_presets: list[dict]):
def from_vnish(
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
) -> MiningModeConfig:
try:
mode_settings = web_settings["miner"]["overclock"]
except KeyError:
return cls.default()
if mode_settings["preset"] == "disabled":
return MiningModeManual.from_vnish(mode_settings)
return cls.manual.from_vnish(mode_settings, web_presets, web_perf_summary)
else:
return MiningModePreset.from_vnish(mode_settings, web_presets)
return cls.preset.from_vnish(mode_settings, web_presets, web_perf_summary)
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
def from_boser(cls, grpc_miner_conf: dict) -> MiningModeConfig:
try:
tuner_conf = grpc_miner_conf["tuner"]
if not tuner_conf.get("enabled", False):
@@ -667,7 +814,7 @@ class MiningModeConfig(MinerConfigOption):
return cls.default()
@classmethod
def from_auradine(cls, web_mode: dict):
def from_auradine(cls, web_mode: dict) -> MiningModeConfig:
try:
mode_data = web_mode["Mode"][0]
if mode_data.get("Sleep") == "on":
@@ -684,9 +831,33 @@ class MiningModeConfig(MinerConfigOption):
return cls.power_tuning(power=mode_data["Power"])
except LookupError:
return cls.default()
return cls.default()
@classmethod
def from_mara(cls, web_config: dict):
def from_btminer_v3(
cls, rpc_device_info: dict, rpc_settings: dict
) -> MiningModeConfig:
try:
is_mining = rpc_device_info["msg"]["miner"]["working"] == "true"
if not is_mining:
return cls.sleep()
power_limit = rpc_settings["msg"]["power-limit"]
if not power_limit == 0:
return cls.power_tuning(power=power_limit)
power_mode = rpc_settings["msg"]["power-mode"]
if power_mode == "normal":
return cls.normal()
if power_mode == "high":
return cls.high()
if power_mode == "low":
return cls.low()
except LookupError:
return cls.default()
return cls.default()
@classmethod
def from_mara(cls, web_config: dict) -> MiningModeConfig:
try:
mode = web_config["mode"]["work-mode-selector"]
if mode == "Fixed":
@@ -711,24 +882,26 @@ class MiningModeConfig(MinerConfigOption):
return cls.default()
@classmethod
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict):
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModeConfig:
preset_info = MiningModePreset.from_luxos(rpc_config, rpc_profiles)
return cls.preset(
active_preset=preset_info.active_preset,
available_presets=preset_info.available_presets,
)
def as_btminer_v3(self) -> dict:
"""Delegate to the default instance for btminer v3 configuration."""
return self.default().as_btminer_v3()
MiningMode = TypeVar(
"MiningMode",
bound=Union[
MiningModeNormal,
MiningModeHPM,
MiningModeLPM,
MiningModeSleep,
MiningModeManual,
MiningModePowerTune,
MiningModeHashrateTune,
MiningModePreset,
],
bound=MiningModeNormal
| MiningModeHPM
| MiningModeLPM
| MiningModeSleep
| MiningModeManual
| MiningModePowerTune
| MiningModeHashrateTune
| MiningModePreset,
)

View File

@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TypeVar, Union
from dataclasses import field
from typing import Any, TypeVar
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@@ -41,26 +41,26 @@ class TunerAlgo(MinerConfigOption):
chip_tune = ChipTuneAlgo
@classmethod
def default(cls) -> TunerAlgoType:
def default(cls) -> StandardTuneAlgo:
return cls.standard()
@classmethod
def from_dict(cls, dict_conf: dict | None) -> TunerAlgoType:
def from_dict(
cls, dict_conf: dict[Any, Any] | None
) -> StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo:
if dict_conf is None:
return cls.default()
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
cls_attr = getattr(cls, mode)
cls_attr = getattr(cls, mode, None)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)
return cls.default()
TunerAlgoType = TypeVar(
"TunerAlgoType",
bound=Union[
StandardTuneAlgo,
VOptAlgo,
BoardTuneAlgo,
ChipTuneAlgo,
],
bound=StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo,
)

View File

@@ -24,7 +24,14 @@ class MiningPreset(MinerConfigValue):
hashrate = None
else:
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"
modded_psu = web_preset["modded_psu_required"]
return cls(

View File

@@ -15,8 +15,6 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
@@ -25,7 +23,9 @@ class ScalingShutdown(MinerConfigValue):
duration: int | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown":
def from_dict(cls, dict_conf: dict | None) -> ScalingShutdown:
if dict_conf is None:
return cls()
return cls(
enabled=dict_conf.get("enabled", False), duration=dict_conf.get("duration")
)
@@ -53,7 +53,7 @@ class ScalingShutdown(MinerConfigValue):
return None
def as_bosminer(self) -> dict:
cfg = {"shutdown_enabled": self.enabled}
cfg: dict[str, bool | int] = {"shutdown_enabled": self.enabled}
if self.duration is not None:
cfg["shutdown_duration"] = self.duration
@@ -70,7 +70,9 @@ class ScalingConfig(MinerConfigValue):
shutdown: ScalingShutdown | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig":
def from_dict(cls, dict_conf: dict | None) -> ScalingConfig:
if dict_conf is None:
return cls()
cls_conf = {
"step": dict_conf.get("step"),
"minimum": dict_conf.get("minimum"),

View File

@@ -17,7 +17,7 @@ from __future__ import annotations
import random
import string
from typing import List
from typing import Any
from pydantic import Field
@@ -43,6 +43,20 @@ class Pool(MinerConfigValue):
"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:
return {
f"pool_{idx}": self.url,
@@ -50,7 +64,17 @@ class Pool(MinerConfigValue):
f"passwd_{idx}": self.password,
}
def as_am_old(self, idx: int = 1, user_suffix: str | None = None) -> dict:
def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
return {
"pool": self.url,
"worker": f"{self.user}{user_suffix or ''}",
"passwd": self.password,
}
def as_am_old(
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
) -> dict:
idx = args[0] if args else kwargs.get("idx", 1)
return {
f"_ant_pool{idx}url": self.url,
f"_ant_pool{idx}user": f"{self.user}{user_suffix or ''}",
@@ -67,7 +91,10 @@ class Pool(MinerConfigValue):
def as_avalon(self, user_suffix: str | None = None) -> str:
return ",".join([self.url, f"{self.user}{user_suffix or ''}", self.password])
def as_inno(self, idx: int = 1, user_suffix: str | None = None) -> dict:
def as_inno(
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
) -> dict:
idx = args[0] if args else kwargs.get("idx", 1)
return {
f"Pool{idx}": self.url,
f"UserName{idx}": f"{self.user}{user_suffix or ''}",
@@ -88,7 +115,7 @@ class Pool(MinerConfigValue):
"pass": self.password,
}
def as_epic(self, user_suffix: str | None = None):
def as_epic(self, user_suffix: str | None = None) -> dict:
return {
"pool": self.url,
"login": f"{self.user}{user_suffix or ''}",
@@ -102,7 +129,7 @@ class Pool(MinerConfigValue):
"pass": self.password,
}
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
def as_espminer(self, user_suffix: str | None = None) -> dict:
return {
"stratumURL": self.url,
"stratumUser": f"{self.user}{user_suffix or ''}",
@@ -125,50 +152,68 @@ class Pool(MinerConfigValue):
}
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "Pool":
def from_dict(cls, dict_conf: dict | None) -> Pool:
if dict_conf is None:
raise ValueError("dict_conf cannot be None")
return cls(
url=dict_conf["url"], user=dict_conf["user"], password=dict_conf["password"]
)
@classmethod
def from_api(cls, api_pool: dict) -> "Pool":
def from_api(cls, api_pool: dict) -> Pool:
return cls(url=api_pool["URL"], user=api_pool["User"], password="x")
@classmethod
def from_epic(cls, api_pool: dict) -> "Pool":
def from_btminer_v3(cls, api_pool: dict) -> Pool:
return cls(url=api_pool["url"], user=api_pool["account"], password="x")
@classmethod
def from_epic(cls, api_pool: dict) -> Pool:
return cls(
url=api_pool["pool"], user=api_pool["login"], password=api_pool["password"]
)
@classmethod
def from_am_modern(cls, web_pool: dict) -> "Pool":
def from_am_modern(cls, web_pool: dict) -> Pool:
return cls(
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
@classmethod
def from_goldshell(cls, web_pool: dict) -> "Pool":
def from_goldshell(cls, web_pool: dict) -> Pool:
return cls(
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
@classmethod
def from_inno(cls, web_pool: dict) -> "Pool":
def from_inno(cls, web_pool: dict) -> Pool:
return cls(
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
@classmethod
def from_bosminer(cls, toml_pool_conf: dict) -> "Pool":
def from_bosminer(cls, toml_pool_conf: dict) -> Pool:
return cls(
url=toml_pool_conf["url"],
user=toml_pool_conf["user"],
password=toml_pool_conf["password"],
password=toml_pool_conf.get("password", ""),
)
@classmethod
def from_vnish(cls, web_pool: dict) -> "Pool":
def from_vnish(cls, web_pool: dict) -> Pool:
return cls(
url="stratum+tcp://" + web_pool["url"],
user=web_pool["user"],
@@ -176,7 +221,7 @@ class Pool(MinerConfigValue):
)
@classmethod
def from_boser(cls, grpc_pool: dict) -> "Pool":
def from_boser(cls, grpc_pool: dict) -> Pool:
return cls(
url=grpc_pool["url"],
user=grpc_pool["user"],
@@ -184,7 +229,7 @@ class Pool(MinerConfigValue):
)
@classmethod
def from_mara(cls, web_pool: dict) -> "Pool":
def from_mara(cls, web_pool: dict) -> Pool:
return cls(
url=web_pool["url"],
user=web_pool["user"],
@@ -192,7 +237,7 @@ class Pool(MinerConfigValue):
)
@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']}"
return cls(
url=url,
@@ -201,11 +246,11 @@ class Pool(MinerConfigValue):
)
@classmethod
def from_luxos(cls, rpc_pools: dict) -> "Pool":
def from_luxos(cls, rpc_pools: dict) -> Pool:
return cls.from_api(rpc_pools)
@classmethod
def from_iceriver(cls, web_pool: dict) -> "Pool":
def from_iceriver(cls, web_pool: dict) -> Pool:
return cls(
url=web_pool["addr"],
user=web_pool["user"],
@@ -235,31 +280,54 @@ class PoolGroup(MinerConfigValue):
idx += 1
return pools
def as_wm(self, user_suffix: str | None = None) -> dict:
pools = {}
def as_hiveon_modern(self, user_suffix: str | None = None) -> list:
pools = []
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.update(
**self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix)
)
pools.append(self.pools[idx].as_hiveon_modern(user_suffix=user_suffix))
else:
pools.update(**Pool(url="", user="", password="").as_wm(idx=idx + 1))
pools.append(Pool(url="", user="", password="").as_hiveon_modern())
idx += 1
return pools
def as_am_old(self, user_suffix: str | None = None) -> dict:
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, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> dict:
pools: dict[str, str] = {}
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.update(**self.pools[idx].as_wm(idx + 1, user_suffix=user_suffix))
else:
pools.update(**Pool(url="", user="", password="").as_wm(idx + 1))
idx += 1
return pools
def as_btminer_v3(self, user_suffix: str | None = None) -> list:
return [pool.as_btminer_v3(user_suffix) for pool in self.pools[:3]]
def as_am_old(
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
) -> dict:
pools: dict[str, str] = {}
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.update(
**self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix)
**self.pools[idx].as_am_old(idx + 1, user_suffix=user_suffix)
)
else:
pools.update(
**Pool(url="", user="", password="").as_am_old(idx=idx + 1)
)
pools.update(**Pool(url="", user="", password="").as_am_old(idx + 1))
idx += 1
return pools
@@ -271,22 +339,24 @@ class PoolGroup(MinerConfigValue):
return self.pools[0].as_avalon(user_suffix=user_suffix)
return Pool(url="", user="", password="").as_avalon()
def as_inno(self, user_suffix: str | None = None) -> dict:
pools = {}
def as_inno(
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
) -> dict:
pools: dict[str, str] = {}
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.update(
**self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix)
**self.pools[idx].as_inno(idx + 1, user_suffix=user_suffix)
)
else:
pools.update(**Pool(url="", user="", password="").as_inno(idx=idx + 1))
pools.update(**Pool(url="", user="", password="").as_inno(idx + 1))
idx += 1
return pools
def as_bosminer(self, user_suffix: str | None = None) -> dict:
if len(self.pools) > 0:
conf = {
conf: dict[str, Any] = {
"name": self.name,
"pool": [
pool.as_bosminer(user_suffix=user_suffix) for pool in self.pools
@@ -306,12 +376,12 @@ class PoolGroup(MinerConfigValue):
def as_mara(self, user_suffix: str | None = None) -> list:
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
def as_espminer(self, user_suffix: str | None = None) -> dict:
return self.pools[0].as_espminer(user_suffix=user_suffix)
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
return PoolGroupConfiguration(
name=self.name,
name=self.name or "",
quota=Quota(value=self.quota),
pools=[p.as_boser() for p in self.pools],
)
@@ -320,7 +390,10 @@ class PoolGroup(MinerConfigValue):
return {"pools": [p.as_vnish(user_suffix=user_suffix) for p in self.pools]}
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
def from_dict(cls, dict_conf: dict | None) -> PoolGroup:
if dict_conf is None:
return cls()
cls_conf = {}
if dict_conf.get("quota") is not None:
@@ -331,36 +404,57 @@ class PoolGroup(MinerConfigValue):
return cls(**cls_conf)
@classmethod
def from_api(cls, api_pool_list: list) -> "PoolGroup":
def from_api(cls, api_pool_list: list) -> PoolGroup:
pools = []
for pool in api_pool_list:
pools.append(Pool.from_api(pool))
return cls(pools=pools)
@classmethod
def from_epic(cls, api_pool_list: list) -> "PoolGroup":
def from_btminer_v3(cls, api_pool_list: list) -> PoolGroup:
pools = []
for pool in api_pool_list:
pools.append(Pool.from_btminer_v3(pool))
return cls(pools=pools)
@classmethod
def from_epic(cls, api_pool_list: list) -> PoolGroup:
pools = []
for pool in api_pool_list:
pools.append(Pool.from_epic(pool))
return cls(pools=pools)
@classmethod
def from_am_modern(cls, web_pool_list: list) -> "PoolGroup":
def from_am_modern(cls, web_pool_list: list) -> PoolGroup:
pools = []
for pool in web_pool_list:
pools.append(Pool.from_am_modern(pool))
return cls(pools=pools)
@classmethod
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
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
def from_goldshell(cls, web_pools: list) -> PoolGroup:
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
@classmethod
def from_inno(cls, web_pools: list) -> "PoolGroup":
def from_inno(cls, web_pools: list) -> PoolGroup:
return cls(pools=[Pool.from_inno(p) for p in web_pools])
@classmethod
def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup":
def from_bosminer(cls, toml_group_conf: dict) -> PoolGroup:
if toml_group_conf.get("pool") is not None:
return cls(
name=toml_group_conf["name"],
@@ -370,13 +464,13 @@ class PoolGroup(MinerConfigValue):
return cls()
@classmethod
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
def from_vnish(cls, web_settings_pools: dict) -> PoolGroup:
return cls(
pools=[Pool.from_vnish(p) for p in web_settings_pools if p["url"] != ""]
)
@classmethod
def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup":
def from_boser(cls, grpc_pool_group: dict) -> PoolGroup:
try:
return cls(
pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]],
@@ -391,15 +485,15 @@ class PoolGroup(MinerConfigValue):
return cls()
@classmethod
def from_mara(cls, web_config_pools: dict) -> "PoolGroup":
def from_mara(cls, web_config_pools: dict) -> PoolGroup:
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
return cls(pools=[Pool.from_bitaxe(web_system_info)])
def from_espminer(cls, web_system_info: dict) -> PoolGroup:
return cls(pools=[Pool.from_espminer(web_system_info)])
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup":
def from_iceriver(cls, web_userpanel: dict) -> PoolGroup:
return cls(
pools=[
Pool.from_iceriver(web_pool)
@@ -409,21 +503,21 @@ class PoolGroup(MinerConfigValue):
class PoolConfig(MinerConfigValue):
groups: List[PoolGroup] = Field(default_factory=list)
groups: list[PoolGroup] = Field(default_factory=list)
@classmethod
def default(cls) -> "PoolConfig":
def default(cls) -> PoolConfig:
return cls()
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolConfig":
def from_dict(cls, dict_conf: dict | None) -> PoolConfig:
if dict_conf is None:
return cls.default()
return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]])
@classmethod
def simple(cls, pools: list[Pool | dict[str, str]]) -> "PoolConfig":
def simple(cls, pools: list[Pool | dict[str, str]]) -> PoolConfig:
group_pools = []
for pool in pools:
if isinstance(pool, dict):
@@ -436,12 +530,29 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_am_modern()}
def as_wm(self, user_suffix: str | None = None) -> dict:
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, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_wm()}
def as_am_old(self, user_suffix: str | None = None) -> dict:
def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_btminer_v3(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_btminer_v3()}
def as_am_old(
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
) -> dict:
if len(self.groups) > 0:
return self.groups[0].as_am_old(user_suffix=user_suffix)
return PoolGroup().as_am_old()
@@ -456,7 +567,9 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_avalon()}
def as_inno(self, user_suffix: str | None = None) -> dict:
def as_inno(
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
) -> dict:
if len(self.groups) > 0:
return self.groups[0].as_inno(user_suffix=user_suffix)
return PoolGroup().as_inno()
@@ -471,7 +584,7 @@ class PoolConfig(MinerConfigValue):
def as_boser(self, user_suffix: str | None = None) -> dict:
return {
"set_pool_groups": SetPoolGroupsRequest(
save_action=SaveAction.SAVE_AND_APPLY,
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
)
}
@@ -507,8 +620,8 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
return {"pools": []}
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
def as_espminer(self, user_suffix: str | None = None) -> dict:
return self.groups[0].as_espminer(user_suffix=user_suffix)
def as_luxos(self, user_suffix: str | None = None) -> dict:
return {}
@@ -517,7 +630,7 @@ class PoolConfig(MinerConfigValue):
return self.groups[0].as_vnish(user_suffix=user_suffix)
@classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig":
def from_api(cls, api_pools: dict) -> PoolConfig:
try:
pool_data = api_pools["POOLS"]
except KeyError:
@@ -527,40 +640,78 @@ class PoolConfig(MinerConfigValue):
return cls(groups=[PoolGroup.from_api(pool_data)])
@classmethod
def from_epic(cls, web_conf: dict) -> "PoolConfig":
def from_btminer_v3(cls, rpc_pools: dict) -> PoolConfig:
try:
pool_data = rpc_pools["pools"]
except KeyError:
return PoolConfig.default()
pool_data = sorted(pool_data, key=lambda x: int(x["id"]))
return cls(groups=[PoolGroup.from_btminer_v3(pool_data)])
@classmethod
def from_epic(cls, web_conf: dict) -> PoolConfig:
pool_data = web_conf["StratumConfigs"]
return cls(groups=[PoolGroup.from_epic(pool_data)])
@classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"]
def from_am_modern(cls, web_conf: dict) -> PoolConfig:
try:
pool_data = web_conf["pools"]
except KeyError:
return cls(groups=[])
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
@classmethod
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
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
def from_goldshell(cls, web_pools: list) -> PoolConfig:
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
@classmethod
def from_inno(cls, web_pools: list) -> "PoolConfig":
def from_goldshell_byte(cls, web_pools: list) -> PoolConfig:
return cls(
groups=[
PoolGroup.from_goldshell(g["pools"])
for g in web_pools
if len(g["pools"]) > 0
]
)
@classmethod
def from_inno(cls, web_pools: list) -> PoolConfig:
return cls(groups=[PoolGroup.from_inno(web_pools)])
@classmethod
def from_bosminer(cls, toml_conf: dict) -> "PoolConfig":
def from_bosminer(cls, toml_conf: dict) -> PoolConfig:
if toml_conf.get("group") is None:
return cls()
return cls(groups=[PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
@classmethod
def from_vnish(cls, web_settings: dict) -> "PoolConfig":
def from_vnish(cls, web_settings: dict) -> PoolConfig:
try:
return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])])
except LookupError:
return cls()
@classmethod
def from_boser(cls, grpc_miner_conf: dict) -> "PoolConfig":
def from_boser(cls, grpc_miner_conf: dict) -> PoolConfig:
try:
return cls(
groups=[
@@ -572,19 +723,19 @@ class PoolConfig(MinerConfigValue):
return cls()
@classmethod
def from_mara(cls, web_config: dict) -> "PoolConfig":
def from_mara(cls, web_config: dict) -> PoolConfig:
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_bitaxe(web_system_info)])
def from_espminer(cls, web_system_info: dict) -> PoolConfig:
return cls(groups=[PoolGroup.from_espminer(web_system_info)])
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":
def from_iceriver(cls, web_userpanel: dict) -> PoolConfig:
return cls(groups=[PoolGroup.from_iceriver(web_userpanel)])
@classmethod
def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> "PoolConfig":
def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> PoolConfig:
return cls(
groups=[
PoolGroup(

View File

@@ -15,8 +15,6 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
@@ -42,7 +40,7 @@ class TemperatureConfig(MinerConfigValue):
return {"temp_control": temp_cfg}
def as_epic(self) -> dict:
temps_config = {"temps": {}, "fans": {"Auto": {}}}
temps_config: dict = {"temps": {}, "fans": {"Auto": {}}}
if self.target is not None:
temps_config["fans"]["Auto"]["Target Temperature"] = self.target
else:
@@ -60,7 +58,9 @@ class TemperatureConfig(MinerConfigValue):
return {"misc": {"restart_temp": self.danger}}
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
def from_dict(cls, dict_conf: dict | None) -> TemperatureConfig:
if dict_conf is None:
return cls()
return cls(
target=dict_conf.get("target"),
hot=dict_conf.get("hot"),
@@ -68,7 +68,7 @@ class TemperatureConfig(MinerConfigValue):
)
@classmethod
def from_bosminer(cls, toml_conf: dict) -> "TemperatureConfig":
def from_bosminer(cls, toml_conf: dict) -> TemperatureConfig:
temp_control = toml_conf.get("temp_control")
if temp_control is not None:
return cls(
@@ -79,7 +79,7 @@ class TemperatureConfig(MinerConfigValue):
return cls()
@classmethod
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
def from_epic(cls, web_conf: dict) -> TemperatureConfig:
try:
dangerous_temp = web_conf["Misc"]["Critical Temp"]
except KeyError:
@@ -97,7 +97,7 @@ class TemperatureConfig(MinerConfigValue):
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp)
@classmethod
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":
def from_vnish(cls, web_settings: dict) -> TemperatureConfig:
try:
dangerous_temp = web_settings["misc"]["restart_temp"]
except KeyError:
@@ -113,7 +113,7 @@ class TemperatureConfig(MinerConfigValue):
return cls()
@classmethod
def from_boser(cls, grpc_miner_conf: dict) -> "TemperatureConfig":
def from_boser(cls, grpc_miner_conf: dict) -> TemperatureConfig:
try:
temperature_conf = grpc_miner_conf["temperature"]
except KeyError:
@@ -144,7 +144,7 @@ class TemperatureConfig(MinerConfigValue):
return cls.default()
@classmethod
def from_luxos(cls, rpc_tempctrl: dict) -> "TemperatureConfig":
def from_luxos(cls, rpc_tempctrl: dict) -> TemperatureConfig:
try:
tempctrl_config = rpc_tempctrl["TEMPCTRL"][0]
return cls(

View File

@@ -15,8 +15,9 @@
# ------------------------------------------------------------------------------
import copy
import time
from collections.abc import Callable
from datetime import datetime, timezone
from typing import Any, List, Union
from typing import Any
from pydantic import BaseModel, Field, computed_field
@@ -26,10 +27,10 @@ from pyasic.data.pools import PoolMetrics, Scheme
from pyasic.device.algorithm.hashrate import AlgoHashRateType
from pyasic.device.algorithm.hashrate.base import GenericHashrate
from ..device.algorithm.hashrate.unit.base import GenericUnit
from .boards import HashBoard
from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .error_codes.base import BaseMinerError
from .fans import Fan
@@ -71,6 +72,7 @@ class MinerData(BaseModel):
errors: A list of errors on the miner.
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_fract: Same as efficiency, but is not rounded to integer. Calculated automatically.
is_mining: Whether the miner is mining.
pools: A list of PoolMetrics instances, each representing metrics for a pool.
"""
@@ -83,13 +85,16 @@ class MinerData(BaseModel):
# about
device_info: DeviceInfo | None = None
serial_number: str | None = None
mac: str | None = None
api_ver: str | None = None
fw_ver: str | None = None
hostname: str | None = None
# hashrate
raw_hashrate: AlgoHashRateType = Field(exclude=True, default=None, repr=False)
raw_hashrate: AlgoHashRateType | None = Field(
exclude=True, default=None, repr=False
)
# sticker
sticker_hashrate: AlgoHashRateType | None = None
@@ -109,25 +114,18 @@ class MinerData(BaseModel):
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False)
# fans
fans: List[Fan] = Field(default_factory=list)
fans: list[Fan] = Field(default_factory=list)
fan_psu: int | None = None
# boards
hashboards: List[HashBoard] = Field(default_factory=list)
hashboards: list[HashBoard] = Field(default_factory=list)
# config
config: MinerConfig | None = None
fault_light: bool | None = None
# errors
errors: List[
Union[
WhatsminerError,
BraiinsOSError,
X19Error,
InnosiliconError,
]
] = Field(default_factory=list)
errors: list[BaseMinerError] = Field(default_factory=list)
# mining state
is_mining: bool = True
@@ -137,8 +135,10 @@ class MinerData(BaseModel):
pools: list[PoolMetrics] = Field(default_factory=list)
@classmethod
def fields(cls):
return list(cls.model_fields.keys())
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):
try:
@@ -198,7 +198,7 @@ class MinerData(BaseModel):
setattr(cp, key, item & other_item)
return cp
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def hashrate(self) -> AlgoHashRateType | None:
if len(self.hashboards) > 0:
@@ -207,14 +207,24 @@ class MinerData(BaseModel):
if item.hashrate is not None:
hr_data.append(item.hashrate)
if len(hr_data) > 0:
return sum(hr_data, start=self.device_info.algo.hashrate(rate=0))
if self.device_info is not None and self.device_info.algo is not None:
from pyasic.device.algorithm.hashrate.unit.base import GenericUnit
return sum(
hr_data,
start=self.device_info.algo.hashrate(
rate=0, unit=GenericUnit.H
),
)
else:
return sum(hr_data, start=GenericHashrate(rate=0))
return self.raw_hashrate
@hashrate.setter
def hashrate(self, val):
self.raw_hashrate = val
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def wattage_limit(self) -> int | None:
if self.config is not None:
@@ -226,7 +236,7 @@ class MinerData(BaseModel):
def wattage_limit(self, val: int):
self.raw_wattage_limit = val
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def total_chips(self) -> int | None:
if len(self.hashboards) > 0:
@@ -237,15 +247,16 @@ class MinerData(BaseModel):
if len(chip_data) > 0:
return sum(chip_data)
return None
return 0
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def nominal(self) -> bool | None:
if self.total_chips is None or self.expected_chips is None:
return None
return self.expected_chips == self.total_chips
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def percent_expected_chips(self) -> int | None:
if self.total_chips is None or self.expected_chips is None:
@@ -254,7 +265,7 @@ class MinerData(BaseModel):
return 0
return round((self.total_chips / self.expected_chips) * 100)
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def percent_expected_hashrate(self) -> int | None:
if self.hashrate is None or self.expected_hashrate is None:
@@ -264,7 +275,7 @@ class MinerData(BaseModel):
except ZeroDivisionError:
return 0
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def percent_expected_wattage(self) -> int | None:
if self.wattage_limit is None or self.wattage is None:
@@ -274,10 +285,10 @@ class MinerData(BaseModel):
except ZeroDivisionError:
return 0
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def temperature_avg(self) -> int | None:
total_temp = 0
total_temp: float = 0
temp_count = 0
for hb in self.hashboards:
if hb.temp is not None:
@@ -287,49 +298,65 @@ class MinerData(BaseModel):
return None
return round(total_temp / temp_count)
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def efficiency(self) -> int | None:
efficiency = self._efficiency(0)
if efficiency is None:
return None
else:
return int(efficiency)
@computed_field # type: ignore[prop-decorator]
@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:
return None
try:
return round(self.wattage / float(self.hashrate))
return round(self.wattage / float(self.hashrate), ndigits)
except ZeroDivisionError:
return 0
return 0.0
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def datetime(self) -> str:
return self.raw_datetime.isoformat()
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def timestamp(self) -> int:
return int(time.mktime(self.raw_datetime.timetuple()))
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def make(self) -> str:
if self.device_info.make is not None:
def make(self) -> str | None:
if self.device_info is not None and self.device_info.make is not None:
return str(self.device_info.make)
return ""
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def model(self) -> str:
if self.device_info.model is not None:
def model(self) -> str | None:
if self.device_info is not None and self.device_info.model is not None:
return str(self.device_info.model)
return ""
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def firmware(self) -> str:
if self.device_info.firmware is not None:
def firmware(self) -> str | None:
if self.device_info is not None and self.device_info.firmware is not None:
return str(self.device_info.firmware)
return ""
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def algo(self) -> str:
if self.device_info.algo is not None:
def algo(self) -> str | None:
if self.device_info is not None and self.device_info.algo is not None:
return str(self.device_info.algo)
return ""
def keys(self) -> list:
return list(self.model_fields.keys())
@@ -367,7 +394,9 @@ class MinerData(BaseModel):
data_list = [str(data[item]) for item in data]
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/).
Parameters:
@@ -376,54 +405,127 @@ class MinerData(BaseModel):
Returns:
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):
func = serialization_map_instance[dt]
item_serialized = func(
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: dict[type, Callable[[str, Any], str | None]] = {
AlgoHashRateType: serialize_algo_hash_rate,
BaseMinerError: serialize_miner_error,
}
serialization_map: dict[type, Callable[[str, Any], str | None]] = {
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 = []
tags = ["ip", "mac", "model", "hostname"]
for attribute in self.fields():
if attribute in tags:
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
tag_data.append(f"{attribute}={escaped_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(field, field_val)
if serialized is not None:
field_data.append(serialized)
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(
f"hashboard_{idx+1}_temperature={item.get('temp', 0)}"
)
field_data.append(
f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}"
)
field_data.append(f"hashboard_{idx+1}_chips={item.get('chips', 0)}")
field_data.append(
f"hashboard_{idx+1}_expected_chips={item.get('expected_chips', 0)}"
)
elif attribute == "fans":
for idx, item in enumerate(self[attribute]):
if item.speed is not None:
field_data.append(f"fan_{idx+1}={item.speed}")
for datatype in serialization_map_instance:
if serialized is None:
if isinstance(field_val, datatype):
func = serialization_map_instance[datatype]
serialized = func(field, field_val)
if serialized is not None:
field_data.append(serialized)
tags_str = ",".join(tag_data)
field_str = ",".join(field_data)
timestamp = str(self.timestamp * 1e9)
tags_str = ",".join(tag_data).replace(" ", "\\ ")
field_str = ",".join(field_data).replace(" ", "\\ ")
timestamp = str(self.timestamp * 10**9)
return " ".join([tags_str, field_str, timestamp])

View File

@@ -15,9 +15,10 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from collections.abc import Callable
from typing import Any
from pydantic import BaseModel, field_serializer
from pydantic import BaseModel
from pyasic.device.algorithm.hashrate import AlgoHashRateType
@@ -28,6 +29,8 @@ class HashBoard(BaseModel):
Attributes:
slot: The slot of the board as an int.
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.
chip_temp: The temperature of the chips as an int.
chips: The chip count of the board as an int.
@@ -41,6 +44,8 @@ class HashBoard(BaseModel):
slot: int = 0
hashrate: AlgoHashRateType | None = None
inlet_temp: float | None = None
outlet_temp: float | None = None
temp: float | None = None
chip_temp: float | None = None
chips: int | None = None
@@ -51,6 +56,12 @@ class HashBoard(BaseModel):
active: bool | None = None
voltage: float | None = None
@classmethod
def fields(cls) -> set:
all_fields = set(cls.model_fields.keys())
all_fields.update(set(cls.model_computed_fields.keys()))
return all_fields
def get(self, __key: str, default: Any = None):
try:
val = self.__getitem__(__key)
@@ -65,3 +76,65 @@ class HashBoard(BaseModel):
return getattr(self, item)
except AttributeError:
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) -> str:
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: Callable[[str, Any], str | None] = (
serialization_map.get(
type(field_val),
lambda _k, _v: None, # type: ignore
)
)
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 .innosilicon import InnosiliconError
from .vnish import VnishError
from .whatsminer import WhatsminerError
from .X19 import X19Error
MinerErrorData = TypeVar(
"MinerErrorData", WhatsminerError, BraiinsOSError, X19Error, InnosiliconError
"MinerErrorData",
WhatsminerError,
BraiinsOSError,
X19Error,
InnosiliconError,
VnishError,
)

View File

@@ -2,6 +2,8 @@ from pydantic import BaseModel
class BaseMinerError(BaseModel):
error_code: int | None = None
@classmethod
def fields(cls):
return list(cls.model_fields.keys())
@@ -16,3 +18,21 @@ class BaseMinerError(BaseModel):
A dictionary version of this class.
"""
return self.asdict()
def as_influxdb(self, root_key: str, level_delimiter: str = ".") -> str:
field_data = []
if self.error_code is not None:
field_data.append(
f"{root_key}{level_delimiter}error_code={self.error_code}"
)
# Check if error_message exists as an attribute (either regular or computed field)
if hasattr(self, "error_message"):
error_message = getattr(self, "error_message")
if error_message is not None:
field_data.append(
f'{root_key}{level_delimiter}error_message="{error_message}"'
)
return ",".join(field_data)

View File

@@ -30,7 +30,7 @@ class InnosiliconError(BaseMinerError):
error_code: int
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def error_message(self) -> str: # noqa - Skip PyCharm inspection
if self.error_code in ERROR_CODES:

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

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pydantic import computed_field
from pyasic.data.error_codes.base import BaseMinerError
@@ -28,50 +29,69 @@ class WhatsminerError(BaseMinerError):
error_code: int
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def error_message(self) -> str: # noqa - Skip PyCharm inspection
if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1":
err_type = int(str(self.error_code)[:2])
err_subtype = int(str(self.error_code)[2:3])
err_value = int(str(self.error_code)[3:])
error_str = str(self.error_code)
# Handle edge cases for short error codes
if len(error_str) < 3:
return "Unknown error type."
if len(error_str) == 6 and not error_str[:1] == "1":
err_type = int(error_str[:2])
err_subtype = int(error_str[2:3])
err_value = int(error_str[3:])
else:
err_type = int(str(self.error_code)[:-2])
err_subtype = int(str(self.error_code)[-2:-1])
err_value = int(str(self.error_code)[-1:])
err_type = int(error_str[:-2])
err_subtype = int(error_str[-2:-1])
err_value = int(error_str[-1:])
try:
select_err_type = ERROR_CODES[err_type]
select_err_type = ERROR_CODES.get(err_type)
if select_err_type is None:
return "Unknown error type."
if err_subtype in select_err_type:
select_err_subtype = select_err_type[err_subtype]
if err_value in select_err_subtype:
return select_err_subtype[err_value]
elif "n" in select_err_subtype:
return select_err_subtype[
"n" # noqa: picks up `select_err_subtype["n"]` as not being numeric?
].replace("{n}", str(err_value))
if isinstance(select_err_subtype, dict):
if err_value in select_err_subtype:
result = select_err_subtype[err_value]
return str(result) if not isinstance(result, str) else result
elif "n" in select_err_subtype:
template = select_err_subtype["n"]
if isinstance(template, str):
return template.replace("{n}", str(err_value))
else:
return "Unknown error type."
else:
return "Unknown error type."
else:
return "Unknown error type."
elif "n" in select_err_type:
select_err_subtype = select_err_type[
"n" # noqa: picks up `select_err_subtype["n"]` as not being numeric?
]
if err_value in select_err_subtype:
return select_err_subtype[err_value]
elif "c" in select_err_subtype:
return (
select_err_subtype["c"]
.replace( # noqa: picks up `select_err_subtype["n"]` as not being numeric?
"{n}", str(err_subtype)
)
.replace("{c}", str(err_value))
)
select_err_subtype = select_err_type["n"]
if isinstance(select_err_subtype, dict):
if err_value in select_err_subtype:
result = select_err_subtype[err_value]
return str(result) if not isinstance(result, str) else result
elif "c" in select_err_subtype:
template = select_err_subtype["c"]
if isinstance(template, str):
return template.replace("{n}", str(err_subtype)).replace(
"{c}", str(err_value)
)
else:
return "Unknown error type."
else:
return "Unknown error type."
else:
return "Unknown error type."
else:
return "Unknown error type."
except KeyError:
except (KeyError, TypeError):
return "Unknown error type."
ERROR_CODES = {
ERROR_CODES: dict[int, dict[int | str, str | dict[int | str, str]]] = {
1: { # Fan error
0: {
0: "Fan unknown.",

View File

@@ -26,7 +26,7 @@ class Fan(BaseModel):
speed: The speed of the fan.
"""
speed: int = None
speed: int | None = None
def get(self, __key: str, default: Any = None):
try:

View File

@@ -1,5 +1,6 @@
from collections.abc import Callable
from enum import Enum
from typing import Optional
from typing import Any
from urllib.parse import urlparse
from pydantic import BaseModel, computed_field, model_serializer
@@ -16,7 +17,7 @@ class PoolUrl(BaseModel):
scheme: Scheme
host: str
port: int
pubkey: Optional[str] = None
pubkey: str | None = None
@model_serializer
def serialize(self):
@@ -39,6 +40,8 @@ class PoolUrl(BaseModel):
scheme = Scheme.STRATUM_V1
host = parsed_url.hostname
port = parsed_url.port
if port is None:
return None
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
@@ -70,16 +73,20 @@ class PoolMetrics(BaseModel):
index: int | None = None
user: str | None = None
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of rejected shares"""
if self.rejected is None or self.accepted is None:
return 0.0
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
@computed_field # type: ignore[misc]
@computed_field # type: ignore[prop-decorator]
@property
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of stale shares."""
if self.get_failures is None or self.accepted is None or self.rejected is None:
return 0.0
return self._calculate_percentage(
self.get_failures, self.accepted + self.rejected
)
@@ -87,8 +94,54 @@ class PoolMetrics(BaseModel):
@staticmethod
def _calculate_percentage(value: int, total: int) -> float:
"""Calculate the percentage."""
if value is None or total is None:
return 0
if total == 0:
return 0
return 0.0
return (value / total) * 100
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_pool_url(key: str, value: PoolUrl) -> str:
return f'{key}="{str(value)}"'
def serialize_bool(key: str, value: bool) -> str:
return f"{key}={str(value).lower()}"
serialization_map: dict[type, Callable[[str, Any], str]] = {
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)
if field_val is None:
continue
serialization_func = serialization_map.get(type(field_val))
if serialization_func is not 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

@@ -1,5 +1,6 @@
from .base import MinerAlgoType
from .blake256 import Blake256Algo
from .blockflow import BlockFlowAlgo
from .eaglesong import EaglesongAlgo
from .equihash import EquihashAlgo
from .ethash import EtHashAlgo
@@ -11,6 +12,7 @@ from .kheavyhash import KHeavyHashAlgo
from .scrypt import ScryptAlgo
from .sha256 import SHA256Algo
from .x11 import X11Algo
from .zksnark import ZkSnarkAlgo
class MinerAlgo:
@@ -24,3 +26,5 @@ class MinerAlgo:
EAGLESONG = EaglesongAlgo
ETHASH = EtHashAlgo
EQUIHASH = EquihashAlgo
BLOCKFLOW = BlockFlowAlgo
ZKSNARK = ZkSnarkAlgo

View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import BlockFlowHashRate
from .hashrate.unit import BlockFlowUnit
class BlockFlowAlgo(MinerAlgoType):
hashrate: type[BlockFlowHashRate] = BlockFlowHashRate
unit: type[BlockFlowUnit] = BlockFlowUnit
name = "BlockFlow"

View File

@@ -1,5 +1,6 @@
from .base import AlgoHashRateType
from .blake256 import Blake256HashRate
from .blockflow import BlockFlowHashRate
from .eaglesong import EaglesongHashRate
from .equihash import EquihashHashRate
from .ethash import EtHashHashRate
@@ -9,6 +10,7 @@ from .kheavyhash import KHeavyHashHashRate
from .scrypt import ScryptHashRate
from .sha256 import SHA256HashRate
from .x11 import X11HashRate
from .zksnark import ZkSnarkHashRate
class AlgoHashRate:
@@ -22,3 +24,5 @@ class AlgoHashRate:
EAGLESONG = EaglesongHashRate
ETHASH = EtHashHashRate
EQUIHASH = EquihashHashRate
BLOCKFLOW = BlockFlowHashRate
ZKSNARK = ZkSnarkHashRate

View File

@@ -1,23 +1,26 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Generic, TypeVar
from pydantic import BaseModel, field_serializer
from typing_extensions import Self
from .unit.base import AlgoHashRateUnitType, GenericUnit
UnitType = TypeVar("UnitType", bound=AlgoHashRateUnitType)
class AlgoHashRateType(BaseModel, ABC):
unit: AlgoHashRateUnitType
class AlgoHashRateType(BaseModel, ABC, Generic[UnitType]):
unit: UnitType
rate: float
@field_serializer("unit")
def serialize_unit(self, unit: AlgoHashRateUnitType):
def serialize_unit(self, unit: UnitType):
return unit.model_dump()
@abstractmethod
def into(self, other: "AlgoHashRateUnitType"):
def into(self, other: UnitType) -> Self:
pass
def auto_unit(self):
@@ -46,7 +49,7 @@ class AlgoHashRateType(BaseModel, ABC):
def __repr__(self):
return f"{self.rate} {str(self.unit)}"
def __round__(self, n: int = None):
def __round__(self, n: int | None = None):
return round(self.rate, n)
def __add__(self, other: Self | int | float) -> Self:
@@ -85,11 +88,11 @@ class AlgoHashRateType(BaseModel, ABC):
return self.__class__(rate=self.rate * other, unit=self.unit)
class GenericHashrate(AlgoHashRateType):
class GenericHashrate(AlgoHashRateType[GenericUnit]):
rate: float = 0
unit: GenericUnit = GenericUnit.H
def into(self, other: GenericUnit):
def into(self, other: GenericUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.blake256 import Blake256Unit
from .unit import HashUnit
class Blake256HashRate(AlgoHashRateType):
class Blake256HashRate(AlgoHashRateType[Blake256Unit]):
rate: float
unit: Blake256Unit = HashUnit.BLAKE256.default

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.blockflow import BlockFlowUnit
from .unit import HashUnit
class BlockFlowHashRate(AlgoHashRateType[BlockFlowUnit]):
rate: float
unit: BlockFlowUnit = HashUnit.BLOCKFLOW.default
def into(self, other: BlockFlowUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.eaglesong import EaglesongUnit
from .unit import HashUnit
class EaglesongHashRate(AlgoHashRateType):
class EaglesongHashRate(AlgoHashRateType[EaglesongUnit]):
rate: float
unit: EaglesongUnit = HashUnit.EAGLESONG.default

View File

@@ -8,9 +8,9 @@ from pyasic.device.algorithm.hashrate.unit.equihash import EquihashUnit
from .unit import HashUnit
class EquihashHashRate(AlgoHashRateType):
class EquihashHashRate(AlgoHashRateType[EquihashUnit]):
rate: float
unit: EquihashUnit = HashUnit.ETHASH.default
unit: EquihashUnit = HashUnit.EQUIHASH.default
def into(self, other: EquihashUnit) -> Self:
return self.__class__(

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.ethash import EtHashUnit
from .unit import HashUnit
class EtHashHashRate(AlgoHashRateType):
class EtHashHashRate(AlgoHashRateType[EtHashUnit]):
rate: float
unit: EtHashUnit = HashUnit.ETHASH.default

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.handshake import HandshakeUnit
from .unit import HashUnit
class HandshakeHashRate(AlgoHashRateType):
class HandshakeHashRate(AlgoHashRateType[HandshakeUnit]):
rate: float
unit: HandshakeUnit = HashUnit.HANDSHAKE.default

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.kadena import KadenaUnit
from .unit import HashUnit
class KadenaHashRate(AlgoHashRateType):
class KadenaHashRate(AlgoHashRateType[KadenaUnit]):
rate: float
unit: KadenaUnit = HashUnit.KADENA.default

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.kheavyhash import KHeavyHashUnit
from .unit import HashUnit
class KHeavyHashHashRate(AlgoHashRateType):
class KHeavyHashHashRate(AlgoHashRateType[KHeavyHashUnit]):
rate: float
unit: KHeavyHashUnit = HashUnit.KHEAVYHASH.default

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit
from .unit import HashUnit
class ScryptHashRate(AlgoHashRateType):
class ScryptHashRate(AlgoHashRateType[ScryptUnit]):
rate: float
unit: ScryptUnit = HashUnit.SCRYPT.default

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.sha256 import SHA256Unit
from .unit import HashUnit
class SHA256HashRate(AlgoHashRateType):
class SHA256HashRate(AlgoHashRateType[SHA256Unit]):
rate: float
unit: SHA256Unit = HashUnit.SHA256.default

View File

@@ -1,4 +1,5 @@
from .blake256 import Blake256Unit
from .blockflow import BlockFlowUnit
from .eaglesong import EaglesongUnit
from .equihash import EquihashUnit
from .ethash import EtHashUnit
@@ -8,6 +9,7 @@ from .kheavyhash import KHeavyHashUnit
from .scrypt import ScryptUnit
from .sha256 import SHA256Unit
from .x11 import X11Unit
from .zksnark import ZkSnarkUnit
class HashUnit:
@@ -21,3 +23,5 @@ class HashUnit:
EAGLESONG = EaglesongUnit
ETHASH = EtHashUnit
EQUIHASH = EquihashUnit
BLOCKFLOW = BlockFlowUnit
ZKSNARK = ZkSnarkUnit

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ from pyasic.device.algorithm.hashrate.unit.x11 import X11Unit
from .unit import HashUnit
class X11HashRate(AlgoHashRateType):
class X11HashRate(AlgoHashRateType[X11Unit]):
rate: float
unit: X11Unit = HashUnit.X11.default

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.zksnark import ZkSnarkUnit
from .unit import HashUnit
class ZkSnarkHashRate(AlgoHashRateType[ZkSnarkUnit]):
rate: float
unit: ZkSnarkUnit = HashUnit.ZKSNARK.default
def into(self, other: ZkSnarkUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import ZkSnarkHashRate
from .hashrate.unit import ZkSnarkUnit
class ZkSnarkAlgo(MinerAlgoType):
hashrate: type[ZkSnarkHashRate] = ZkSnarkHashRate
unit: type[ZkSnarkUnit] = ZkSnarkUnit
name = "zkSNARK"

View File

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

View File

@@ -43,6 +43,7 @@ class AntminerModels(MinerModelType):
S19jNoPIC = "S19j No PIC"
S19ProPlus = "S19 Pro+"
S19jPro = "S19j Pro"
S19jPlus = "S19j+"
S19jProNoPIC = "S19j Pro No PIC"
S19jProPlus = "S19j Pro+"
S19jProPlusNoPIC = "S19j Pro+ No PIC"
@@ -54,12 +55,17 @@ class AntminerModels(MinerModelType):
S19ProPlusHydro = "S19 Pro+ Hydro"
S19KPro = "S19K Pro"
S19kPro = "S19k Pro"
S19ProA = "S19 Pro A"
S19kProNoPIC = "S19k Pro No PIC"
S19jXP = "S19j XP"
T19 = "T19"
S21 = "S21"
S21Plus = "S21+"
S21PlusHydro = "S21+ Hydro"
S21Pro = "S21 Pro"
S21Hydro = "S21 Hydro"
T21 = "T21"
S19XPHydro = "S19 XP Hydro"
def __str__(self):
return self.value
@@ -218,6 +224,7 @@ class WhatsminerModels(MinerModelType):
M31V20 = "M31 V20"
M32V10 = "M32 V10"
M32V20 = "M32 V20"
M32S = "M32S"
M33SPlusPlusVG40 = "M33S++ VG40"
M33SPlusPlusVH20 = "M33S++ VH20"
M33SPlusPlusVH30 = "M33S++ VH30"
@@ -447,7 +454,10 @@ class AvalonminerModels(MinerModelType):
Avalon1166Pro = "Avalon 1166 Pro"
Avalon1126Pro = "Avalon 1126 Pro"
Avalon1246 = "Avalon 1246"
Avalon1566 = "Avalon 1566"
AvalonNano3 = "Avalon Nano 3"
AvalonNano3s = "Avalon Nano 3s"
AvalonQHome = "Avalon Q Home"
def __str__(self):
return self.value
@@ -470,6 +480,8 @@ class GoldshellModels(MinerModelType):
KDMax = "KD Max"
KDBoxII = "KD Box II"
KDBoxPro = "KD Box Pro"
Byte = "Byte"
MiniDoge = "Mini Doge"
def __str__(self):
return self.value
@@ -478,7 +490,9 @@ class GoldshellModels(MinerModelType):
class ePICModels(MinerModelType):
BM520i = "BlockMiner 520i"
BM720i = "BlockMiner 720i"
eLITE1 = "BlockMiner eLITE 1.0"
S19jProDual = "S19j Pro Dual"
S19kProDual = "S19k Pro Dual"
def __str__(self):
return self.value
@@ -507,6 +521,14 @@ class BitAxeModels(MinerModelType):
return self.value
class LuckyMinerModels(MinerModelType):
LV07 = "LV07"
LV08 = "LV08"
def __str__(self):
return self.value
class IceRiverModels(MinerModelType):
KS0 = "KS0"
KS1 = "KS1"
@@ -517,6 +539,7 @@ class IceRiverModels(MinerModelType):
KS5 = "KS5"
KS5L = "KS5L"
KS5M = "KS5M"
AL3 = "AL3"
def __str__(self):
return self.value
@@ -540,6 +563,18 @@ class BraiinsModels(MinerModelType):
BMM100 = "BMM100"
BMM101 = "BMM101"
def __str__(self):
return self.value
class ElphapexModels(MinerModelType):
DG1 = "DG1"
DG1Plus = "DG1+"
DG1Home = "DG1Home"
def __str__(self):
return self.value
class MinerModel:
ANTMINER = AntminerModels
@@ -550,7 +585,9 @@ class MinerModel:
AURADINE = AuradineModels
EPIC = ePICModels
BITAXE = BitAxeModels
LUCKYMINER = LuckyMinerModels
ICERIVER = IceRiverModels
HAMMER = HammerModels
VOLCMINER = VolcMinerModels
ELPHAPEX = ElphapexModels
BRAIINS = BraiinsModels

View File

@@ -1,352 +0,0 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import asyncio
from typing import List, Union
from pyasic.errors import APIError
from pyasic.miners import AnyMiner
from pyasic.miners.backends import AntminerModern, BOSMiner, BTMiner
from pyasic.miners.device.models import (
S9,
S17,
T17,
S17e,
S17Plus,
S17Pro,
T17e,
T17Plus,
)
FAN_USAGE = 50 # 50 W per fan
class MinerLoadBalancer:
"""A load balancer for miners. Can be passed a list of `AnyMiner`, or a list of phases (lists of `AnyMiner`)."""
def __init__(
self,
phases: Union[List[List[AnyMiner]], None] = None,
):
self.phases = [_MinerPhaseBalancer(phase) for phase in phases]
async def balance(self, wattage: int) -> int:
phase_wattage = wattage // len(self.phases)
setpoints = await asyncio.gather(
*[phase.get_balance_setpoints(phase_wattage) for phase in self.phases]
)
tasks = []
total_wattage = 0
for setpoint in setpoints:
wattage_set = 0
for miner in setpoint:
if setpoint[miner]["set"] == "on":
wattage_set += setpoint[miner]["max"]
tasks.append(setpoint[miner]["miner"].resume_mining())
elif setpoint[miner]["set"] == "off":
wattage_set += setpoint[miner]["min"]
tasks.append(setpoint[miner]["miner"].stop_mining())
else:
wattage_set += setpoint[miner]["set"]
tasks.append(
setpoint[miner]["miner"].set_power_limit(setpoint[miner]["set"])
)
total_wattage += wattage_set
await asyncio.gather(*tasks)
return total_wattage
class _MinerPhaseBalancer:
def __init__(self, miners: List[AnyMiner]):
self.miners = {
str(miner.ip): {
"miner": miner,
"set": 0,
"min": miner.expected_fans * FAN_USAGE,
}
for miner in miners
}
for miner in miners:
if (
isinstance(miner, BTMiner)
and not (miner.raw_model.startswith("M2") if miner.raw_model else True)
) or isinstance(miner, BOSMiner):
if isinstance(miner, S9):
self.miners[str(miner.ip)]["tune"] = True
self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 1400
elif True in [
isinstance(miner, x)
for x in [S17, S17Plus, S17Pro, S17e, T17, T17Plus, T17e]
]:
self.miners[str(miner.ip)]["tune"] = True
self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 2400
else:
self.miners[str(miner.ip)]["tune"] = True
self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 3600
elif isinstance(miner, AntminerModern):
self.miners[str(miner.ip)]["tune"] = False
self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 3600
elif isinstance(miner, BTMiner):
self.miners[str(miner.ip)]["tune"] = False
self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 3600
if miner.raw_model:
if miner.raw_model.startswith("M2"):
self.miners[str(miner.ip)]["tune"] = False
self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 2400
else:
self.miners[str(miner.ip)]["tune"] = False
self.miners[str(miner.ip)]["shutdown"] = False
self.miners[str(miner.ip)]["max"] = 3600
self.miners[str(miner.ip)]["min"] = 3600
async def balance(self, wattage: int) -> int:
setpoint = await self.get_balance_setpoints(wattage)
wattage_set = 0
tasks = []
for miner in setpoint:
if setpoint[miner]["set"] == "on":
wattage_set += setpoint[miner]["max"]
tasks.append(setpoint[miner]["miner"].resume_mining())
elif setpoint[miner]["set"] == "off":
wattage_set += setpoint[miner]["min"]
tasks.append(setpoint[miner]["miner"].stop_mining())
else:
wattage_set += setpoint[miner]["set"]
tasks.append(
setpoint[miner]["miner"].set_power_limit(setpoint[miner]["set"])
)
await asyncio.gather(*tasks)
return wattage_set
async def get_balance_setpoints(self, wattage: int) -> dict:
# gather data needed to optimize shutdown only miners
dp = ["hashrate", "wattage", "wattage_limit", "hashboards"]
data = await asyncio.gather(
*[
self.miners[miner]["miner"].get_data(data_to_get=dp)
for miner in self.miners
]
)
pct_expected_list = [d.percent_ideal for d in data]
pct_ideal = 0
if len(pct_expected_list) > 0:
pct_ideal = sum(pct_expected_list) / len(pct_expected_list)
wattage = round(wattage * 1 / (pct_ideal / 100))
for data_point in data:
if (not self.miners[data_point.ip]["tune"]) and (
not self.miners[data_point.ip]["shutdown"]
):
# cant do anything with it so need to find a semi-accurate power limit
if data_point.wattage_limit is not None:
self.miners[data_point.ip]["max"] = int(data_point.wattage_limit)
self.miners[data_point.ip]["min"] = int(data_point.wattage_limit)
elif data_point.wattage is not None:
self.miners[data_point.ip]["max"] = int(data_point.wattage)
self.miners[data_point.ip]["min"] = int(data_point.wattage)
max_tune_wattage = sum(
[miner["max"] for miner in self.miners.values() if miner["tune"]]
)
max_shutdown_wattage = sum(
[
miner["max"]
for miner in self.miners.values()
if (not miner["tune"]) and (miner["shutdown"])
]
)
max_other_wattage = sum(
[
miner["max"]
for miner in self.miners.values()
if (not miner["tune"]) and (not miner["shutdown"])
]
)
min_tune_wattage = sum(
[miner["min"] for miner in self.miners.values() if miner["tune"]]
)
min_shutdown_wattage = sum(
[
miner["min"]
for miner in self.miners.values()
if (not miner["tune"]) and (miner["shutdown"])
]
)
# min_other_wattage = sum(
# [
# miner["min"]
# for miner in self.miners.values()
# if (not miner["tune"]) and (not miner["shutdown"])
# ]
# )
# make sure wattage isnt set too high
if wattage > (max_tune_wattage + max_shutdown_wattage + max_other_wattage):
raise APIError(
f"Wattage setpoint is too high, setpoint: {wattage}W, max: {max_tune_wattage + max_shutdown_wattage + max_other_wattage}W"
)
# should now know wattage limits and which can be tuned/shutdown
# check if 1/2 max of the miners which can be tuned is low enough
if (max_tune_wattage / 2) + max_shutdown_wattage + max_other_wattage < wattage:
useable_wattage = wattage - (max_other_wattage + max_shutdown_wattage)
useable_miners = len(
[m for m in self.miners.values() if (m["set"] == 0) and (m["tune"])]
)
if not useable_miners == 0:
watts_per_miner = useable_wattage // useable_miners
# loop through and set useable miners to wattage
for miner in self.miners:
if (self.miners[miner]["set"] == 0) and (
self.miners[miner]["tune"]
):
self.miners[miner]["set"] = watts_per_miner
elif self.miners[miner]["set"] == 0 and (
self.miners[miner]["shutdown"]
):
self.miners[miner]["set"] = "on"
# check if shutting down miners will help
elif (
max_tune_wattage / 2
) + min_shutdown_wattage + max_other_wattage < wattage:
# tuneable inclusive since could be S9 BOS+ and S19 Stock, would rather shut down the S9, tuneable should always support shutdown
useable_wattage = wattage - (
min_tune_wattage + max_other_wattage + min_shutdown_wattage
)
for miner in sorted(
[miner for miner in self.miners.values() if miner["shutdown"]],
key=lambda x: x["max"],
reverse=True,
):
if miner["tune"]:
miner_min_watt_use = miner["max"] / 2
useable_wattage -= miner_min_watt_use - miner["min"]
if useable_wattage < 0:
useable_wattage += miner_min_watt_use - miner["min"]
self.miners[str(miner["miner"].ip)]["set"] = "off"
else:
miner_min_watt_use = miner["max"]
useable_wattage -= miner_min_watt_use - miner["min"]
if useable_wattage < 0:
useable_wattage += miner_min_watt_use - miner["min"]
self.miners[str(miner["miner"].ip)]["set"] = "off"
new_shutdown_wattage = sum(
[
miner["max"] if miner["set"] == 0 else miner["min"]
for miner in self.miners.values()
if miner["shutdown"] and not miner["tune"]
]
)
new_tune_wattage = sum(
[
miner["min"]
for miner in self.miners.values()
if miner["tune"] and miner["set"] == "off"
]
)
useable_wattage = wattage - (
new_tune_wattage + max_other_wattage + new_shutdown_wattage
)
useable_miners = len(
[m for m in self.miners.values() if (m["set"] == 0) and (m["tune"])]
)
if not useable_miners == 0:
watts_per_miner = useable_wattage // useable_miners
# loop through and set useable miners to wattage
for miner in self.miners:
if (self.miners[miner]["set"] == 0) and (
self.miners[miner]["tune"]
):
self.miners[miner]["set"] = watts_per_miner
elif self.miners[miner]["set"] == 0 and (
self.miners[miner]["shutdown"]
):
self.miners[miner]["set"] = "on"
# check if shutting down tuneable miners will do it
elif min_tune_wattage + min_shutdown_wattage + max_other_wattage < wattage:
# all miners that can be shutdown need to be
for miner in self.miners:
if (not self.miners[miner]["tune"]) and (
self.miners[miner]["shutdown"]
):
self.miners[miner]["set"] = "off"
# calculate wattage usable by tuneable miners
useable_wattage = wattage - (
min_tune_wattage + max_other_wattage + min_shutdown_wattage
)
# loop through miners to see how much is actually useable
# sort the largest first
for miner in sorted(
[
miner
for miner in self.miners.values()
if miner["tune"] and miner["shutdown"]
],
key=lambda x: x["max"],
reverse=True,
):
# add min to useable wattage since it was removed earlier, and remove 1/2 tuner max
useable_wattage -= (miner["max"] / 2) - miner["min"]
if useable_wattage < 0:
useable_wattage += (miner["max"] / 2) - miner["min"]
self.miners[str(miner["miner"].ip)]["set"] = "off"
new_tune_wattage = sum(
[
miner["min"]
for miner in self.miners.values()
if miner["tune"] and miner["set"] == "off"
]
)
useable_wattage = wattage - (
new_tune_wattage + max_other_wattage + min_shutdown_wattage
)
useable_miners = len(
[m for m in self.miners.values() if (m["set"] == 0) and (m["tune"])]
)
if not useable_miners == 0:
watts_per_miner = useable_wattage // useable_miners
# loop through and set useable miners to wattage
for miner in self.miners:
if (self.miners[miner]["set"] == 0) and (
self.miners[miner]["tune"]
):
self.miners[miner]["set"] = watts_per_miner
elif self.miners[miner]["set"] == 0 and (
self.miners[miner]["shutdown"]
):
self.miners[miner]["set"] = "on"
else:
raise APIError(
f"Wattage setpoint is too low, setpoint: {wattage}W, min: {min_tune_wattage + min_shutdown_wattage + max_other_wattage}W"
) # PhaseBalancingError(f"Wattage setpoint is too low, setpoint: {wattage}W, min: {min_tune_wattage + min_shutdown_wattage + max_other_wattage}W")
return self.miners

View File

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

View File

@@ -25,7 +25,9 @@ from pyasic.miners.device.models import (
S19i,
S19j,
S19jNoPIC,
S19jPlus,
S19jPro,
S19jProPlus,
S19jXP,
S19KPro,
S19Plus,
@@ -80,6 +82,10 @@ class BMMinerS19jPro(AntminerModern, S19jPro):
pass
class BMMinerS19jPlus(AntminerModern, S19jPlus):
pass
class BMMinerS19L(AntminerModern, S19L):
pass
@@ -102,3 +108,7 @@ class BMMinerS19KPro(AntminerModern, S19KPro):
class BMMinerS19jXP(AntminerModern, S19jXP):
pass
class BMMinerS19jProPlus(AntminerModern, S19jProPlus):
pass

View File

@@ -22,7 +22,9 @@ from .S19 import (
BMMinerS19i,
BMMinerS19j,
BMMinerS19jNoPIC,
BMMinerS19jPlus,
BMMinerS19jPro,
BMMinerS19jProPlus,
BMMinerS19jXP,
BMMinerS19KPro,
BMMinerS19L,

View File

@@ -15,12 +15,24 @@
# ------------------------------------------------------------------------------
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):
pass
class BMMinerS21Plus(AntminerModern, S21Plus):
pass
class BMMinerS21PlusHydro(AntminerModern, S21PlusHydro):
pass
class BMMinerS21Pro(AntminerModern, S21Pro):
pass
class BMMinerS21Hydro(AntminerModern, S21Hydro):
pass

View File

@@ -13,5 +13,11 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .S21 import BMMinerS21, BMMinerS21Pro
from .S21 import (
BMMinerS21,
BMMinerS21Hydro,
BMMinerS21Plus,
BMMinerS21PlusHydro,
BMMinerS21Pro,
)
from .T21 import BMMinerT21

View File

@@ -30,6 +30,7 @@ from pyasic.miners.device.models import (
S19Plus,
S19Pro,
S19ProPlusHydro,
S19XPHydro,
)
@@ -87,3 +88,7 @@ class BOSMinerS19XP(BOSer, S19XP):
class BOSMinerS19ProPlusHydro(BOSer, S19ProPlusHydro):
pass
class BOSMinerS19XPHydro(BOSer, S19XPHydro):
pass

View File

@@ -29,5 +29,6 @@ from .S19 import (
BOSMinerS19Pro,
BOSMinerS19ProPlusHydro,
BOSMinerS19XP,
BOSMinerS19XPHydro,
)
from .T19 import BOSMinerT19

View File

@@ -15,8 +15,40 @@
# ------------------------------------------------------------------------------
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):
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. -
# ------------------------------------------------------------------------------
from .S21 import BOSMinerS21
from .S21 import (
BOSMinerS21,
BOSMinerS21Hydro,
BOSMinerS21Plus,
BOSMinerS21PlusHydro,
BOSMinerS21Pro,
)
from .T21 import BOSMinerT21

View File

@@ -14,7 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.device.models import MinerModel
from pyasic.device.models import MinerModel, MinerModelType
from pyasic.miners.backends import ePIC
from pyasic.miners.device.models import (
S19,
@@ -56,6 +56,12 @@ class ePICS19XP(ePIC, S19XP):
class ePICS19jProDual(ePIC, S19jPro):
raw_model = MinerModel.EPIC.S19jProDual
raw_model: MinerModelType = MinerModel.EPIC.S19jProDual
expected_fans = S19jPro.expected_fans * 2
expected_hashboards = S19jPro.expected_hashboards * 2
class ePICS19kProDual(ePIC, S19kPro):
raw_model: MinerModelType = 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,
ePICS19jProPlus,
ePICS19kPro,
ePICS19kProDual,
ePICS19Pro,
ePICS19XP,
)

View File

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

View File

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

View File

@@ -14,9 +14,6 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import List, Optional
import asyncssh
from pyasic.data import HashBoard
from pyasic.device.algorithm import AlgoHashRate, HashUnit
@@ -78,7 +75,7 @@ class HiveonT9(HiveonOld, T9):
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]:
hashboards = [
HashBoard(slot=board, expected_chips=self.expected_chips)
for board in range(self.expected_hashboards)
@@ -86,7 +83,7 @@ class HiveonT9(HiveonOld, T9):
if rpc_stats is None:
try:
rpc_stats = self.rpc.stats()
rpc_stats = await self.rpc.stats()
except APIError:
return []
@@ -100,7 +97,7 @@ class HiveonT9(HiveonOld, T9):
hashrate = 0
chips = 0
for chipset in board_map[board]:
if hashboards[board].chip_temp is None:
if hashboards[board].chip_temp is None and rpc_stats is not None:
try:
hashboards[board].temp = rpc_stats["STATS"][1][f"temp{chipset}"]
hashboards[board].chip_temp = rpc_stats["STATS"][1][
@@ -110,11 +107,12 @@ class HiveonT9(HiveonOld, T9):
pass
else:
hashboards[board].missing = False
try:
hashrate += rpc_stats["STATS"][1][f"chain_rate{chipset}"]
chips += rpc_stats["STATS"][1][f"chain_acn{chipset}"]
except (KeyError, IndexError):
pass
if rpc_stats is not None:
try:
hashrate += rpc_stats["STATS"][1][f"chain_rate{chipset}"]
chips += rpc_stats["STATS"][1][f"chain_acn{chipset}"]
except (KeyError, IndexError):
pass
hashboards[board].hashrate = AlgoHashRate.SHA256(
rate=float(hashrate), unit=HashUnit.SHA256.GH
).into(self.algo.unit.default)
@@ -122,8 +120,8 @@ class HiveonT9(HiveonOld, T9):
return hashboards
async def _get_env_temp(self, rpc_stats: dict = None) -> Optional[float]:
env_temp_list = []
async def _get_env_temp(self, rpc_stats: dict | None = None) -> float | None:
env_temp_list: list[int] = []
board_map = {
0: [2, 9, 10],
1: [3, 11, 12],
@@ -146,3 +144,4 @@ class HiveonT9(HiveonOld, T9):
if not env_temp_list == []:
return round(sum(env_temp_list) / len(env_temp_list))
return None

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 T21
class LUXMinerT21(LUXMiner, T21):
pass

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------
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,13 +20,16 @@ from pyasic.miners.device.models import (
S19XP,
S19a,
S19aPro,
S19Hydro,
S19i,
S19j,
S19jPro,
S19kPro,
S19NoPIC,
S19Pro,
S19ProA,
S19ProHydro,
S19XPHydro,
)
@@ -42,10 +45,18 @@ class VNishS19Pro(VNish, S19Pro):
pass
class VNishS19Hydro(VNish, S19Hydro):
pass
class VNishS19XP(VNish, S19XP):
pass
class VNishS19XPHydro(VNish, S19XPHydro):
pass
class VNishS19a(VNish, S19a):
pass
@@ -54,6 +65,10 @@ class VNishS19aPro(VNish, S19aPro):
pass
class VNishS19ProA(VNish, S19ProA):
pass
class VNishS19i(VNish, S19i):
pass

View File

@@ -18,13 +18,16 @@ from .S19 import (
VNishS19,
VNishS19a,
VNishS19aPro,
VNishS19Hydro,
VNishS19i,
VNishS19j,
VNishS19jPro,
VNishS19kPro,
VNishS19NoPIC,
VNishS19Pro,
VNishS19ProA,
VNishS19ProHydro,
VNishS19XP,
VNishS19XPHydro,
)
from .T19 import VNishT19

View File

@@ -15,8 +15,24 @@
# ------------------------------------------------------------------------------
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):
pass
class VNishS21Plus(VNish, S21Plus):
pass
class VNishS21PlusHydro(VNish, S21PlusHydro):
pass
class VNishS21Pro(VNish, S21Pro):
pass
class VNishS21Hydro(VNish, S21Hydro):
pass

View File

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

View File

@@ -18,5 +18,5 @@ from pyasic.miners.backends import VNish
from pyasic.miners.device.models import L3Plus
class VnishL3Plus(VNish, L3Plus):
class VNishL3Plus(VNish, L3Plus):
pass

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .L3 import VnishL3Plus
from .L3 import VNishL3Plus

View File

@@ -18,5 +18,5 @@ from pyasic.miners.backends import VNish
from pyasic.miners.device.models import L7
class VnishL7(VNish, L7):
class VNishL7(VNish, L7):
pass

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .L7 import VnishL7
from .L7 import VNishL7

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 L9
class VNishL9(VNish, L9):
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 .L9 import VNishL9

View File

@@ -16,6 +16,7 @@
from .X3 import *
from .X7 import *
from .X9 import *
from .X17 import *
from .X19 import *
from .X21 import *

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

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