Compare commits

...

112 Commits

Author SHA1 Message Date
Upstream Data
52432e6043 version: bump version number 2024-11-06 09:06:11 -07:00
Upstream Data
727e484860 docs: update docs 2024-11-06 09:05:08 -07:00
Upstream Data
6c091756d2 feature: add support for Iceriver KS5 and submodels 2024-11-06 09:04:38 -07:00
Upstream Data
14533ce4fe version: bump version number 2024-11-05 09:46:35 -07:00
Upstream Data
82d1840039 bug: fix inf and nan in factory 2024-11-05 09:46:11 -07:00
Brett Rowan
8e6240cdba feature: LuxOS fixes and updates (#192)
* feature: add luxos tuner support to config.

* feature: add luxos temp control support to config.

* bug: fix failure to identify luxOS miners sometimes.

* feature: add get_config with temperature parsing.

* bug: fix some handling in hashboards.

* feature: add API version and fw version.

* refactor: improve RPC api handling.

* refactor: remove useless code, and tag bug.

* feature: add fault light check support.

* refactor: improve fanset compatibility.

* feature: add fan config parsing.

* feature: add pools parsing from luxos.

---------

Co-authored-by: Upstream Data <brett@upstreamdata.ca>
2024-11-05 09:43:34 -07:00
pre-commit-ci[bot]
5749e173d1 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v5.0.0)
- [github.com/psf/black: 24.3.0 → 24.10.0](https://github.com/psf/black/compare/24.3.0...24.10.0)
2024-11-05 09:38:40 -07:00
Upstream Data
7d682b62ac version: bump version number 2024-11-05 09:37:18 -07:00
Upstream Data
6739a1001f docs: update supported types 2024-11-05 09:36:58 -07:00
Upstream Data
56e4a5307f feature: add support for Antminer K7 2024-11-05 09:36:27 -07:00
Upstream Data
88de27c9e7 feature: add support for Whatsminer M50VH90 2024-11-05 09:32:30 -07:00
Upstream Data
a77113c4db feature: add support for Innosilicon A11 2024-11-05 09:27:15 -07:00
Upstream Data
c19945bb82 feature: add support for Vnish S19j Pro BB 2024-11-05 09:22:11 -07:00
Upstream Data
1756937d20 feature: add support for Antminer Z15 Pro 2024-11-05 09:18:04 -07:00
Upstream Data
c7b7fe864b feature: improve vnish is_mining functionality
Fixes #225
2024-11-05 09:12:13 -07:00
John-Paul Compagnone
e7ebefd1bf style fix 2024-11-04 09:13:57 -07:00
John-Paul Compagnone
4677efbc46 fix typo for marathon backend 2024-11-04 09:13:57 -07:00
Brett Rowan
4b7a1a0495 bug: fix lockfile failing release 2024-11-01 16:05:59 -06:00
Brett Rowan
cc4e7da4e5 version: bump version number 2024-11-01 16:02:35 -06:00
Brett Rowan
a3d2d7d35e feature: add web parsing for luxOS type 2024-11-01 16:02:15 -06:00
Brett Rowan
d67de98bd0 version: bump version number 2024-11-01 10:52:04 -06:00
Brett Rowan
fd1a3e459b bug: reset betterproto version so that it can be built for pypi 2024-11-01 10:51:49 -06:00
Brett Rowan
adcab694b5 version: bump version number. 2024-11-01 10:46:59 -06:00
Brett Rowan
2bb097272f fix: use git version of better proto for compatibility with home assistant 2024-11-01 10:46:45 -06:00
Upstream Data
896968dded version: bump version number 2024-10-30 14:48:41 -06:00
Upstream Data
56b8f7c5b3 feature: parse iceriver config 2024-10-30 14:48:15 -06:00
Upstream Data
0ed7559aef version: bump version number 2024-10-30 14:37:47 -06:00
Upstream Data
275d87e4fe bug: fix AntminerOld board parsing 2024-10-30 14:37:19 -06:00
Upstream Data
c3ab814d77 version: bump version number 2024-10-30 14:08:38 -06:00
Upstream Data
05a8569205 bug: fix some calcuation errors with pool info 2024-10-30 14:08:22 -06:00
Upstream Data
b098cb8136 version: bump version number 2024-10-30 13:40:44 -06:00
Upstream Data
75fe7857e4 feature: add iceriver pool metrics 2024-10-30 13:39:22 -06:00
Upstream Data
66797aced1 ci: remove deprecated stages arg from pre-commit 2024-10-30 13:20:41 -06:00
Upstream Data
4a71e38078 ci: fix test framework name 2024-10-30 13:19:46 -06:00
Upstream Data
9fb07e4fa3 ci: skip pre-commit CI running pytest 2024-10-30 13:19:12 -06:00
Upstream Data
74792771ec version: bump version number 2024-10-30 13:16:22 -06:00
Upstream Data
fa6e8a976d bug: fix iceriver userpanel parsing 2024-10-30 13:16:07 -06:00
James Hilliard
f20531cff5 Run pre-commit formatting on all files 2024-10-30 13:07:57 -06:00
Upstream Data
8b1cbed9ce version: bump version number. 2024-10-30 12:25:47 -06:00
Upstream Data
0194e13427 bug: update A11MX chip count 2024-10-30 12:25:32 -06:00
Upstream Data
82d71abf54 version: bump version number 2024-10-30 12:23:08 -06:00
Upstream Data
e71cfadf6e bug: add model parser into map for iceriver 2024-10-30 12:22:44 -06:00
Upstream Data
18931c4e98 version: bump version number 2024-10-30 12:18:53 -06:00
Upstream Data
8622c080aa bug: fix inno pool parsing 2024-10-30 12:18:27 -06:00
Upstream Data
cb71b2a593 docs: update supported miners 2024-10-30 10:44:37 -06:00
Upstream Data
ff5956da41 version: bump version number 2024-10-30 10:32:48 -06:00
Upstream Data
acdafc2efd bug: fix hashboard innosilicon model selection 2024-10-30 10:32:29 -06:00
Upstream Data
b8874092ad bug: fix hashboard count on A11MX 2024-10-30 10:07:59 -06:00
Upstream Data
ad28ba0b3e version: bump version number 2024-10-30 09:27:54 -06:00
Upstream Data
0d90b60eef feature: add the rest of the iceriver models 2024-10-30 09:27:12 -06:00
Upstream Data
7c18c9f69c feature: improve iceriver support and add support for KS3M 2024-10-30 09:21:12 -06:00
Upstream Data
975560f46f feature: add iceriver type parsing 2024-10-30 09:12:54 -06:00
Upstream Data
bfe9cbf7d9 bug: fix iceriver miners being identified as bitaxe 2024-10-30 08:54:27 -06:00
Upstream Data
ccb5eb73db version: bump version number 2024-10-30 08:49:25 -06:00
Upstream Data
d143667bd6 feature: add warning message when instantiating an unsupported miner type 2024-10-30 08:49:01 -06:00
Upstream Data
87d809abc0 bug: update KS3 fan count 2024-10-30 08:44:38 -06:00
Upstream Data
4dc5b1a541 feature: add chip count for WM M60VK20 2024-10-30 08:44:08 -06:00
Upstream Data
ddd3e867f9 feature: add stratum+ssl pool url scheme 2024-10-30 08:41:35 -06:00
Upstream Data
77480d3d69 feature: add support for antminer KS5 2024-10-30 08:39:05 -06:00
Upstream Data
0767c93002 feature: add support for antminer KS3 2024-10-30 08:33:59 -06:00
Upstream Data
e690e6dd3b version: bump version number 2024-10-29 16:16:13 -06:00
James Hilliard
d4665ed768 Update betterproto and regenerate protoc files
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2024-10-29 16:15:30 -06:00
James Hilliard
b90a92c0df Update betterproto and regenerate protoc files
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2024-10-29 16:15:11 -06:00
Upstream Data
50cfcf9796 version: bump version number 2024-10-29 09:56:30 -06:00
Brett Rowan
5d204f09da Merge pull request #213 from jameshilliard/update-deps
Update dependencices
2024-10-29 09:56:07 -06:00
James Hilliard
4c0410322f Update dependencices 2024-10-29 09:53:33 -06:00
Upstream Data
fbb2b3f6e7 version: bump version number 2024-10-29 09:46:03 -06:00
Upstream Data
0f09fb49fc bug: fix inf and nan parsing issues by replacing them with 0 2024-10-29 09:35:19 -06:00
Upstream Data
b0d063d6ed feature: add support for Inno A11MX (no chip counts) 2024-10-29 09:15:57 -06:00
Upstream Data
a68fe70af4 bug: fix pool parsing failing with no scheme 2024-10-29 08:57:43 -06:00
Upstream Data
43c7ac281b feature: add support for KA3 2024-10-29 08:29:09 -06:00
Upstream Data
a97ae55a06 version: bump version number 2024-10-28 08:18:52 -06:00
Upstream Data
4a3a6f4186 feature: add get_mac for bitaxe 2024-10-28 08:18:15 -06:00
Brett Rowan
f976724ada version: bump version number 2024-10-25 14:12:34 -06:00
Brett Rowan
2632bdaa30 Merge pull request #208 from eandersson/braiin_fix 2024-10-25 14:11:33 -06:00
Erik Olof Gunnar Andersson
91016d7b8c Fix issue with BraiinsOS health check failing 2024-10-25 22:04:34 +02:00
Upstream Data
2b00e741ca version: bump version number
Fixes: #206
2024-10-08 08:16:25 -06:00
Upstream Data
d496c11d67 bug: fix some cases where Antminer online status couldnt be parsed
Re: #206
2024-10-08 08:16:25 -06:00
Upstream Data
5880223517 bug: fix goldshell issues with pools data 2024-10-08 08:16:25 -06:00
Brett Rowan
394a5dcd0d Merge pull request #204 from Ytemiloluwa/BFGMiner
feat: Add _get_pools method for BFGMiner(StockFirmware)
2024-10-01 08:49:40 -06:00
Upstream Data
7365275f46 version: bump version number. 2024-09-24 12:51:17 -06:00
Upstream Data
0ecab5fdd4 bug: fix an issue with moving board slots on BOS+. 2024-09-24 12:50:56 -06:00
ytemiloluwa
ed0d9f73e4 backends: add _get_pools method to bfgminer 2024-09-19 09:05:29 +01:00
Upstream Data
28f4e16662 version: bump version number. 2024-09-18 13:16:51 -06:00
Upstream Data
b9b0bff946 bug: pin betterproto version to avoid errors with unset oneof variants 2024-09-18 13:16:28 -06:00
Upstream Data
790718a5df bug: fix some issues with BOS+ calls. 2024-09-18 13:05:08 -06:00
Brett Rowan
96a0301f5e Merge pull request #203 from Ytemiloluwa/BTMiner
feat: Add _get_pools method for BTMiner(StockFirmware)
2024-09-17 11:30:14 -06:00
ytemiloluwa
c57b019b7d backends: add _get_pools to BTMiner 2024-09-17 08:55:09 +01:00
Brett Rowan
af920c4dda version: bump version number 2024-09-12 17:28:19 -06:00
Brett Rowan
f3d11788ed bug: fix missing await calls
Fixes #201
2024-09-12 17:27:57 -06:00
Brett Rowan
fd0e02af59 feature: add support for BOSMinerT21 2024-09-12 17:19:08 -06:00
Brett Rowan
2a6c51d52c Merge pull request #188 from Ytemiloluwa/marathon
feat: add _get_pools method for marathon miner
2024-09-04 08:38:29 -06:00
ytemiloluwa
2d62e2070b pool: highest priority 2024-09-04 15:21:39 +01:00
ytemiloluwa
b143bd70f0 updated keys 2024-09-03 22:49:55 +01:00
ytemiloluwa
605509c57c updated keys 2024-09-03 21:35:14 +01:00
Brett Rowan
7036137b23 Merge pull request #189 from 1e9abhi1e10/luxminer_firmware 2024-09-02 22:14:15 -06:00
1e9abhi1e10
7a9ff535b4 Refactor upgrade_firmware to maintain bool return type 2024-09-03 09:42:08 +05:30
Brett Rowan
f185bafe2a version: bump version number. 2024-09-02 21:12:56 -06:00
Brett Rowan
ab81d5d020 feature: add some more whatsminer chip counts. 2024-09-02 21:12:37 -06:00
1e9abhi1e10
0965e6489b return status message in upgrade_firmware function 2024-09-02 23:43:17 +05:30
ytemiloluwa
792e1c9cad corrected parsing 2024-09-02 09:27:30 +01:00
Brett Rowan
a6721f971a version: bump version number. 2024-09-01 16:49:26 -06:00
Brett Rowan
8113d0e4e0 bug: remove print statement. 2024-09-01 16:49:07 -06:00
Brett Rowan
e3c7d3f8a2 version: bump version number. 2024-09-01 16:48:15 -06:00
Brett Rowan
6415de8c73 bug: fix more parsing issues. 2024-09-01 16:47:53 -06:00
Brett Rowan
f2838cf31d bug: fix avalon nano parsing. 2024-09-01 16:41:28 -06:00
Brett Rowan
fbd49b370d version: bump version number. 2024-09-01 16:23:33 -06:00
Brett Rowan
79f7296576 bug: fix some issues with avalonminer parsing. 2024-09-01 16:22:59 -06:00
1e9abhi1e10
21636a75fa made upgrade_firmware to return boolean result 2024-08-23 12:45:20 +05:30
1e9abhi1e10
6fdd156fa3 Added upgraderun in rpc/luxminer 2024-08-21 01:06:50 +05:30
1e9abhi1e10
b957aa7fba feat: Add update firmware for LuxOS Miner 2024-08-20 16:12:33 +05:30
ytemiloluwa
a71aa6868a backends: add _get_pools to marathon 2024-08-19 07:08:22 +01:00
101 changed files with 2229 additions and 935 deletions

View File

@@ -1,12 +1,15 @@
ci:
skip:
- unittest
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.3.0
rev: 24.10.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
@@ -24,4 +27,3 @@ repos:
'types': [python]
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
pass_filenames: false
stages: [commit]

View File

@@ -8,3 +8,10 @@
show_root_heading: false
heading_level: 4
## Z15 Pro (Stock)
::: pyasic.miners.antminer.bmminer.X15.Z15.BMMinerZ15Pro
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -274,6 +274,13 @@
show_root_heading: false
heading_level: 4
## S19j Pro (VNish)
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19a (VNish)
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19a
handler: python

View File

@@ -29,6 +29,13 @@
show_root_heading: false
heading_level: 4
## T21 (BOS+)
::: pyasic.miners.antminer.bosminer.X21.T21.BOSMinerT21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21 (VNish)
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
handler: python

View File

@@ -22,6 +22,20 @@
show_root_heading: false
heading_level: 4
## KA3 (Stock)
::: pyasic.miners.antminer.bmminer.X3.KA3.BMMinerKA3
handler: python
options:
show_root_heading: false
heading_level: 4
## KS3 (Stock)
::: pyasic.miners.antminer.bmminer.X3.KS3.BMMinerKS3
handler: python
options:
show_root_heading: false
heading_level: 4
## L3+ (VNish)
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
handler: python

View File

@@ -8,3 +8,10 @@
show_root_heading: false
heading_level: 4
## KS5 (Stock)
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -8,6 +8,13 @@
show_root_heading: false
heading_level: 4
## K7 (Stock)
::: pyasic.miners.antminer.bmminer.X7.K7.BMMinerK7
handler: python
options:
show_root_heading: false
heading_level: 4
## L7 (VNish)
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
handler: python

View File

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

View File

@@ -1,6 +1,20 @@
# pyasic
## KSX Models
## KS0 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS0.IceRiverKS0
handler: python
options:
show_root_heading: false
heading_level: 4
## KS1 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS1.IceRiverKS1
handler: python
options:
show_root_heading: false
heading_level: 4
## KS2 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
handler: python
@@ -8,3 +22,45 @@
show_root_heading: false
heading_level: 4
## KS3 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3
handler: python
options:
show_root_heading: false
heading_level: 4
## KS3L (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3L
handler: python
options:
show_root_heading: false
heading_level: 4
## KS3M (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3M
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5L (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5L
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5M (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5M
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -0,0 +1,17 @@
# pyasic
## A11X Models
## A11 (Stock)
::: pyasic.miners.innosilicon.cgminer.A11X.A11.InnosiliconA11
handler: python
options:
show_root_heading: false
heading_level: 4
## A11MX (Stock)
::: pyasic.miners.innosilicon.cgminer.A11X.A11M.InnosiliconA11MX
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -21,18 +21,22 @@ details {
<li><a href="../antminer/X3#d3-stock">D3 (Stock)</a></li>
<li><a href="../antminer/X3#hs3-stock">HS3 (Stock)</a></li>
<li><a href="../antminer/X3#l3_1-stock">L3+ (Stock)</a></li>
<li><a href="../antminer/X3#ka3-stock">KA3 (Stock)</a></li>
<li><a href="../antminer/X3#ks3-stock">KS3 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X5 Series:</summary>
<ul>
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X7 Series:</summary>
<ul>
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
<li><a href="../antminer/X7#k7-stock">K7 (Stock)</a></li>
</ul>
</details>
<details>
@@ -49,6 +53,7 @@ details {
<summary>X15 Series:</summary>
<ul>
<li><a href="../antminer/X15#z15-stock">Z15 (Stock)</a></li>
<li><a href="../antminer/X15#z15-pro-stock">Z15 Pro (Stock)</a></li>
</ul>
</details>
<details>
@@ -278,6 +283,7 @@ details {
<li><a href="../whatsminer/M5X#m50-vh60-stock">M50 VH60 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh70-stock">M50 VH70 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh80-stock">M50 VH80 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh90-stock">M50 VH90 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vj10-stock">M50 VJ10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vj20-stock">M50 VJ20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vj30-stock">M50 VJ30 (Stock)</a></li>
@@ -378,6 +384,12 @@ details {
<li><a href="../avalonminer/A12X#avalon-1246-stock">Avalon 1246 (Stock)</a></li>
</ul>
</details>
<details>
<summary>nano Series:</summary>
<ul>
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -395,6 +407,13 @@ details {
<li><a href="../innosilicon/A10X#a10x-stock">A10X (Stock)</a></li>
</ul>
</details>
<details>
<summary>A11X Series:</summary>
<ul>
<li><a href="../innosilicon/A11X#a11-stock">A11 (Stock)</a></li>
<li><a href="../innosilicon/A11X#a11mx-stock">A11MX (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -470,6 +489,7 @@ details {
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
</ul>
</details>
</ul>
@@ -505,6 +525,7 @@ details {
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19a-vnish">S19a (VNish)</a></li>
<li><a href="../antminer/X19#s19a-pro-vnish">S19a Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-hydro-vnish">S19 Pro Hydro (VNish)</a></li>
@@ -661,7 +682,15 @@ details {
<details>
<summary>KSX Series:</summary>
<ul>
<li><a href="../iceriver/KSX#ks0-stock">KS0 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks1-stock">KS1 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks2-stock">KS2 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3-stock">KS3 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3l-stock">KS3L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3m-stock">KS3M (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5-stock">KS5 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5l-stock">KS5L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5m-stock">KS5M (Stock)</a></li>
</ul>
</details>
</ul>

View File

@@ -71,6 +71,13 @@
show_root_heading: false
heading_level: 4
## M50 VH90 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH90
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VJ10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
handler: python

806
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -148,6 +148,14 @@ class MinerConfig:
**self.pools.as_bitaxe(user_suffix=user_suffix),
}
def as_luxos(self, user_suffix: str = None) -> dict:
return {
**self.fan_mode.as_luxos(),
**self.temperature.as_luxos(),
**self.mining_mode.as_luxos(),
**self.pools.as_luxos(user_suffix=user_suffix),
}
@classmethod
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from a dictionary."""
@@ -250,3 +258,21 @@ class MinerConfig:
pools=PoolConfig.from_bitaxe(web_system_info),
fan_mode=FanModeConfig.from_bitaxe(web_system_info),
)
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_iceriver(web_userpanel),
)
@classmethod
def from_luxos(
cls, rpc_tempctrl: dict, rpc_fans: dict, rpc_pools: dict, rpc_groups: dict
) -> "MinerConfig":
return cls(
temperature=TemperatureConfig.from_luxos(rpc_tempctrl=rpc_tempctrl),
fan_mode=FanModeConfig.from_luxos(
rpc_tempctrl=rpc_tempctrl, rpc_fans=rpc_fans
),
pools=PoolConfig.from_luxos(rpc_pools=rpc_pools, rpc_groups=rpc_groups),
)

View File

@@ -63,6 +63,9 @@ class MinerConfigOption(Enum):
def as_bitaxe(self) -> dict:
return self.value.as_bitaxe()
def as_luxos(self) -> dict:
return self.value.as_luxos()
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
@@ -125,6 +128,9 @@ class MinerConfigValue:
def as_bitaxe(self) -> dict:
return {}
def as_luxos(self) -> dict:
return {}
def __getitem__(self, item):
try:
return getattr(self, item)

View File

@@ -83,6 +83,9 @@ class FanModeNormal(MinerConfigValue):
def as_bitaxe(self) -> dict:
return {"autoFanspeed": 1}
def as_luxos(self) -> dict:
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
@dataclass
class FanModeManual(MinerConfigValue):
@@ -144,6 +147,9 @@ class FanModeManual(MinerConfigValue):
def as_bitaxe(self) -> dict:
return {"autoFanspeed": 0, "fanspeed": self.speed}
def as_luxos(self) -> dict:
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
@dataclass
class FanModeImmersion(MinerConfigValue):
@@ -167,6 +173,9 @@ class FanModeImmersion(MinerConfigValue):
def as_mara(self) -> dict:
return {"general-config": {"environment-profile": "OilImmersionCooling"}}
def as_luxos(self) -> dict:
return {"fanset": {"speed": 0, "min_fans": 0}}
class FanModeConfig(MinerConfigOption):
normal = FanModeNormal
@@ -304,3 +313,23 @@ class FanModeConfig(MinerConfigOption):
return cls.normal()
else:
return cls.manual(speed=web_system_info["fanspeed"])
@classmethod
def from_luxos(cls, rpc_fans: dict, rpc_tempctrl: dict):
try:
mode = rpc_tempctrl["TEMPCTRL"][0]["Mode"]
if mode == "Manual":
speed = rpc_fans["FANS"][0]["Speed"]
min_fans = rpc_fans["FANCTRL"][0]["MinFans"]
if min_fans == 0 and speed == 0:
return cls.immersion()
return cls.manual(
speed=speed,
minimum_fans=min_fans,
)
return cls.normal(
minimum_fans=rpc_fans["FANCTRL"][0]["MinFans"],
)
except LookupError:
pass
return cls.default()

View File

@@ -70,6 +70,9 @@ class MiningModeNormal(MinerConfigValue):
}
}
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": False}}
@dataclass
class MiningModeSleep(MinerConfigValue):
@@ -198,7 +201,7 @@ class MiningModePowerTune(MinerConfigValue):
def as_boser(self) -> dict:
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action=SaveAction.SAVE_AND_APPLY,
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
power_target=PowerTargetMode(
@@ -240,6 +243,9 @@ class MiningModePowerTune(MinerConfigValue):
}
}
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": True}}
@dataclass
class MiningModeHashrateTune(MinerConfigValue):
@@ -275,7 +281,7 @@ class MiningModeHashrateTune(MinerConfigValue):
def as_boser(self) -> dict:
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action=SaveAction.SAVE_AND_APPLY,
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
hashrate_target=HashrateTargetMode(
@@ -333,6 +339,9 @@ class MiningModeHashrateTune(MinerConfigValue):
}
}
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": True}}
@dataclass
class ManualBoardSettings(MinerConfigValue):

View File

@@ -222,6 +222,18 @@ class Pool(MinerConfigValue):
password=web_system_info.get("stratumPassword", ""),
)
@classmethod
def from_luxos(cls, rpc_pools: dict) -> "Pool":
return cls.from_api(rpc_pools)
@classmethod
def from_iceriver(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["addr"],
user=web_pool["user"],
password=web_pool["pass"],
)
@dataclass
class PoolGroup(MinerConfigValue):
@@ -402,6 +414,15 @@ class PoolGroup(MinerConfigValue):
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
return cls(pools=[Pool.from_bitaxe(web_system_info)])
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup":
return cls(
pools=[
Pool.from_iceriver(web_pool)
for web_pool in web_userpanel["data"]["pools"]
]
)
@dataclass
class PoolConfig(MinerConfigValue):
@@ -467,7 +488,7 @@ class PoolConfig(MinerConfigValue):
def as_boser(self, user_suffix: str = None) -> dict:
return {
"set_pool_groups": SetPoolGroupsRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action=SaveAction.SAVE_AND_APPLY,
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
)
}
@@ -506,6 +527,9 @@ class PoolConfig(MinerConfigValue):
def as_bitaxe(self, user_suffix: str = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
def as_luxos(self, user_suffix: str = None) -> dict:
return {}
@classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig":
try:
@@ -568,3 +592,24 @@ class PoolConfig(MinerConfigValue):
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_bitaxe(web_system_info)])
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_iceriver(web_userpanel)])
@classmethod
def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> "PoolConfig":
return cls(
groups=[
PoolGroup(
pools=[
Pool.from_luxos(pool)
for pool in rpc_pools["POOLS"]
if pool["GROUP"] == group["GROUP"]
],
name=group["Name"],
quota=group["Quota"],
)
for group in rpc_groups["GROUPS"]
]
)

View File

@@ -54,6 +54,9 @@ class TemperatureConfig(MinerConfigValue):
temps_config["temps"]["shutdown"] = self.hot
return temps_config
def as_luxos(self) -> dict:
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
return cls(
@@ -130,3 +133,16 @@ class TemperatureConfig(MinerConfigValue):
return cls(**conf)
return cls.default()
@classmethod
def from_luxos(cls, rpc_tempctrl: dict) -> "TemperatureConfig":
try:
tempctrl_config = rpc_tempctrl["TEMPCTRL"][0]
return cls(
target=tempctrl_config.get("Target"),
hot=tempctrl_config.get("Hot"),
danger=tempctrl_config.get("Dangerous"),
)
except LookupError:
pass
return cls.default()

View File

@@ -23,13 +23,13 @@ from typing import Any, List, Union
from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune
from pyasic.data.pools import PoolMetrics
from .boards import HashBoard
from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .fans import Fan
from .hashrate import AlgoHashRate, HashUnit
from pyasic.data.pools import PoolMetrics
@dataclass

View File

@@ -7,6 +7,7 @@ from urllib.parse import urlparse
class Scheme(Enum):
STRATUM_V1 = "stratum+tcp"
STRATUM_V2 = "stratum2+tcp"
STRATUM_V1_SSL = "stratum+ssl"
@dataclass
@@ -25,7 +26,10 @@ class PoolUrl:
@classmethod
def from_str(cls, url: str) -> "PoolUrl":
parsed_url = urlparse(url)
scheme = Scheme(parsed_url.scheme)
if not parsed_url.scheme.strip() == "":
scheme = Scheme(parsed_url.scheme)
else:
scheme = Scheme.STRATUM_V1
host = parsed_url.hostname
port = parsed_url.port
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
@@ -85,6 +89,8 @@ class PoolMetrics:
@staticmethod
def _calculate_percentage(value: int, total: int) -> float:
"""Calculate the percentage."""
if value is None or total is None:
return 0
if total == 0:
return 0
return (value / total) * 100

View File

@@ -26,6 +26,7 @@ class MinerMake(str, Enum):
AURADINE = "Auradine"
EPIC = "ePIC"
BITAXE = "BitAxe"
ICERIVER = "IceRiver"
def __str__(self):
return self.value

View File

@@ -5,14 +5,19 @@ class AntminerModels(str, Enum):
D3 = "D3"
HS3 = "HS3"
L3Plus = "L3+"
KA3 = "KA3"
KS3 = "KS3"
DR5 = "DR5"
KS5 = "KS5"
L7 = "L7"
K7 = "K7"
E9Pro = "E9Pro"
S9 = "S9"
S9i = "S9i"
S9j = "S9j"
T9 = "T9"
Z15 = "Z15"
Z15Pro = "Z15 Pro"
S17 = "S17"
S17Plus = "S17+"
S17Pro = "S17 Pro"
@@ -220,6 +225,7 @@ class WhatsminerModels(str, Enum):
M50VH60 = "M50 VH60"
M50VH70 = "M50 VH70"
M50VH80 = "M50 VH80"
M50VH90 = "M50 VH90"
M50VJ10 = "M50 VJ10"
M50VJ20 = "M50 VJ20"
M50VJ30 = "M50 VJ30"
@@ -293,6 +299,8 @@ class AvalonminerModels(str, Enum):
class InnosiliconModels(str, Enum):
T3HPlus = "T3H+"
A10X = "A10X"
A11 = "A11"
A11MX = "A11MX"
def __str__(self):
return self.value
@@ -341,7 +349,15 @@ class BitAxeModels(str, Enum):
class IceRiverModels(str, Enum):
KS0 = "KS0"
KS1 = "KS1"
KS2 = "KS2"
KS3 = "KS3"
KS3L = "KS3L"
KS3M = "KS3M"
KS5 = "KS5"
KS5L = "KS5L"
KS5M = "KS5M"
def __str__(self):
return self.value

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 AntminerModern
from pyasic.miners.device.models import Z15Pro
class BMMinerZ15Pro(AntminerModern, Z15Pro):
pass

View File

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

View File

@@ -23,6 +23,7 @@ from .S19 import (
BMMinerS19j,
BMMinerS19jNoPIC,
BMMinerS19jPro,
BMMinerS19KPro,
BMMinerS19L,
BMMinerS19Plus,
BMMinerS19Pro,
@@ -30,6 +31,5 @@ from .S19 import (
BMMinerS19ProPlus,
BMMinerS19ProPlusHydro,
BMMinerS19XP,
BMMinerS19KPro,
)
from .T19 import BMMinerT19

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 AntminerModern
from pyasic.miners.device.models import KA3
class BMMinerKA3(AntminerModern, KA3):
pass

View File

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

View File

@@ -14,4 +14,6 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .HS3 import BMMinerHS3
from .KA3 import BMMinerKA3
from .KS3 import BMMinerKS3
from .L3 import BMMinerL3Plus

View File

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

View File

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

View File

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

View File

@@ -13,4 +13,5 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .K7 import BMMinerK7
from .L7 import BMMinerL7

View File

@@ -14,8 +14,10 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .X3 import *
from .X5 import *
from .X7 import *
from .X9 import *
from .X15 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 BOSer
from pyasic.miners.device.models import T21
class BOSMinerT21(BOSer, T21):
pass

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------
from .S21 import BOSMinerS21
from .T21 import BOSMinerT21

View File

@@ -15,7 +15,4 @@
# ------------------------------------------------------------------------------
from .S21 import ePICS21, ePICS21Pro
from .T21 import (
ePICT21,
)
from .T21 import ePICT21

View File

@@ -14,9 +14,9 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import List, Optional, Union
from pathlib import Path
import logging
from pathlib import Path
from typing import List, Optional, Union
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
@@ -141,17 +141,24 @@ class AntminerModern(BMMiner):
raise ValueError("File location must be provided for firmware upgrade.")
try:
result = await self.web.update_firmware(file=file, keep_settings=keep_settings)
result = await self.web.update_firmware(
file=file, keep_settings=keep_settings
)
if result.get("success"):
logging.info("Firmware upgrade process completed successfully for AntMiner.")
logging.info(
"Firmware upgrade process completed successfully for AntMiner."
)
return "Firmware upgrade completed successfully."
else:
error_message = result.get("message", "Unknown error")
logging.error(f"Firmware upgrade failed. Response: {error_message}")
return f"Firmware upgrade failed. Response: {error_message}"
except Exception as e:
logging.error(f"An error occurred during the firmware upgrade process: {e}", exc_info=True)
logging.error(
f"An error occurred during the firmware upgrade process: {e}",
exc_info=True,
)
raise
async def fault_light_on(self) -> bool:
@@ -367,7 +374,7 @@ class AntminerModern(BMMiner):
if web_get_conf is not None:
try:
if web_get_conf["bitmain-work-mode"].isdigit():
if str(web_get_conf["bitmain-work-mode"]).isdigit():
return (
False if int(web_get_conf["bitmain-work-mode"]) == 1 else True
)
@@ -590,49 +597,44 @@ class AntminerOld(CGMiner):
pass
if rpc_stats is not None:
try:
board_offset = -1
boards = rpc_stats["STATS"]
if len(boards) > 1:
for board_num in range(1, 16, 5):
for _b_num in range(5):
b = boards[1].get(f"chain_acn{board_num + _b_num}")
board_offset = -1
boards = rpc_stats["STATS"]
if len(boards) > 1:
for board_num in range(1, 16, 5):
for _b_num in range(5):
b = boards[1].get(f"chain_acn{board_num + _b_num}")
if b and not b == 0 and board_offset == -1:
board_offset = board_num
if board_offset == -1:
board_offset = 1
if b and not b == 0 and board_offset == -1:
board_offset = board_num
if board_offset == -1:
board_offset = 1
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
)
for i in range(board_offset, board_offset + self.expected_hashboards):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
if chip_temp:
hashboard.chip_temp = round(chip_temp)
chip_temp = boards[1].get(f"temp{i}")
if chip_temp:
hashboard.chip_temp = round(chip_temp)
temp = boards[1].get(f"temp2_{i}")
if temp:
hashboard.temp = round(temp)
temp = boards[1].get(f"temp2_{i}")
if temp:
hashboard.temp = round(temp)
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.GH
).into(self.algo.unit.default)
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = AlgoHashRate.SHA256(
float(hashrate), HashUnit.SHA256.GH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
except (LookupError, ValueError, TypeError):
pass
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
return hashboards

View File

@@ -193,7 +193,14 @@ class Auradine(StockFirmware):
for key in conf.keys():
await self.web.send_command(command=key, **conf[key])
async def upgrade_firmware(self, *, url: str = None, version: str = "latest", keep_settings: bool = False, **kwargs) -> bool:
async def upgrade_firmware(
self,
*,
url: str = None,
version: str = "latest",
keep_settings: bool = False,
**kwargs,
) -> bool:
"""
Upgrade the firmware of the Auradine device.
@@ -209,7 +216,9 @@ class Auradine(StockFirmware):
logging.info("Starting firmware upgrade process.")
if not url and not version:
raise ValueError("Either URL or version must be provided for firmware upgrade.")
raise ValueError(
"Either URL or version must be provided for firmware upgrade."
)
if url:
result = await self.web.firmware_upgrade(url=url)
@@ -220,11 +229,15 @@ class Auradine(StockFirmware):
logging.info("Firmware upgrade process completed successfully.")
return True
else:
logging.error(f"Firmware upgrade failed: {result.get('error', 'Unknown error')}")
logging.error(
f"Firmware upgrade failed: {result.get('error', 'Unknown error')}"
)
return False
except Exception as e:
logging.error(f"An error occurred during the firmware upgrade process: {str(e)}")
logging.error(
f"An error occurred during the firmware upgrade process: {str(e)}"
)
return False
##################################################

View File

@@ -124,7 +124,6 @@ class AvalonMiner(CGMiner):
data_dict = {}
try:
for key, val in [tuple(item) for item in data_list]:
print(key, val)
data_dict[key] = val
except ValueError:
# --avalon args
@@ -148,10 +147,7 @@ class AvalonMiner(CGMiner):
if raw_data[0] == "":
raw_data = raw_data[1:]
if len(raw_data) == 2:
stats_dict[raw_data[0]] = raw_data[1]
else:
stats_dict[raw_data[0]] = raw_data[1:]
stats_dict[raw_data[0]] = raw_data[1:]
stats_items.append(raw_data)
return stats_dict
@@ -257,7 +253,7 @@ class AvalonMiner(CGMiner):
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return AlgoHashRate.SHA256(
parsed_stats["GHSmm"], HashUnit.SHA256.GH
float(parsed_stats["GHSmm"][0]), HashUnit.SHA256.GH
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass
@@ -273,7 +269,7 @@ class AvalonMiner(CGMiner):
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return float(parsed_stats["Temp"])
return float(parsed_stats["Temp"][0])
except (IndexError, KeyError, ValueError, TypeError):
pass
@@ -288,7 +284,7 @@ class AvalonMiner(CGMiner):
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["MPO"])
return int(parsed_stats["MPO"][0])
except (IndexError, KeyError, ValueError, TypeError):
pass
@@ -309,7 +305,7 @@ class AvalonMiner(CGMiner):
for fan in range(self.expected_fans):
try:
fans_data[fan].speed = int(parsed_stats[f"Fan{fan + 1}"])
fans_data[fan].speed = int(parsed_stats[f"Fan{fan + 1}"][0])
except (IndexError, KeyError, ValueError, TypeError):
pass
return fans_data
@@ -327,7 +323,7 @@ class AvalonMiner(CGMiner):
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
led = int(parsed_stats["Led"])
led = int(parsed_stats["Led"][0])
return True if led == 1 else False
except (IndexError, KeyError, ValueError, TypeError):
pass

View File

@@ -18,6 +18,7 @@ from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -49,6 +50,10 @@ BFGMINER_DATA_LOC = DataLocations(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -207,6 +212,36 @@ class BFGMiner(StockFirmware):
return fans
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data
async def _get_expected_hashrate(
self, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
@@ -228,4 +263,4 @@ class BFGMiner(StockFirmware):
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass
pass

View File

@@ -41,6 +41,10 @@ BITAXE_DATA_LOC = DataLocations(
"_get_api_ver",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_system_info", "system/info")],
),
}
)
@@ -187,3 +191,16 @@ class BitAxe(BaseMiner):
return web_system_info["version"]
except KeyError:
pass
async def _get_mac(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["macAddr"].upper()
except KeyError:
pass

View File

@@ -21,6 +21,7 @@ from typing import List, Optional, Union
import aiofiles
import tomli_w
try:
import tomllib
except ImportError:
@@ -726,9 +727,8 @@ BOSER_DATA_LOC = DataLocations(
[RPCAPICommand("rpc_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("grpc_pool_groups", "get_pool_groups")]
)
"_get_pools", [WebAPICommand("grpc_pool_groups", "get_pool_groups")]
),
}
)
@@ -798,7 +798,7 @@ class BOSer(BraiinsOSFirmware):
async def set_power_limit(self, wattage: int) -> bool:
try:
result = await self.web.set_power_target(
wattage, save_action=SaveAction.SAVE_ACTION_SAVE_AND_FORCE_APPLY
wattage, save_action=SaveAction.SAVE_AND_FORCE_APPLY
)
except APIError:
return False
@@ -926,8 +926,10 @@ class BOSer(BraiinsOSFirmware):
pass
if grpc_hashboards is not None:
for board in grpc_hashboards["hashboards"]:
idx = int(board["id"]) - 1
grpc_boards = sorted(
grpc_hashboards["hashboards"], key=lambda x: int(x["id"])
)
for idx, board in enumerate(grpc_boards):
if board.get("chipsCount") is not None:
hashboards[idx].chips = board["chipsCount"]
if board.get("boardTemp") is not None:
@@ -951,7 +953,7 @@ class BOSer(BraiinsOSFirmware):
async def _get_wattage(self, grpc_miner_stats: dict = None) -> Optional[int]:
if grpc_miner_stats is None:
try:
grpc_miner_stats = self.web.get_miner_stats()
grpc_miner_stats = await self.web.get_miner_stats()
except APIError:
pass
@@ -983,7 +985,7 @@ class BOSer(BraiinsOSFirmware):
async def _get_fans(self, grpc_cooling_state: dict = None) -> List[Fan]:
if grpc_cooling_state is None:
try:
grpc_cooling_state = self.web.get_cooling_state()
grpc_cooling_state = await self.web.get_cooling_state()
except APIError:
pass
@@ -1086,12 +1088,12 @@ class BOSer(BraiinsOSFirmware):
url=pool_info["url"],
user=pool_info["user"],
index=idx,
accepted=pool_info["stats"]["acceptedShares"],
rejected=pool_info["stats"]["rejectedShares"],
get_failures=pool_info["stats"]["stale_shares"],
accepted=pool_info["stats"].get("acceptedShares", 0),
rejected=pool_info["stats"].get("rejectedShares", 0),
get_failures=0,
remote_failures=0,
active=pool_info["active"],
alive=pool_info["alive"]
active=pool_info.get("active", False),
alive=pool_info.get("alive"),
)
pools_data.append(pool_data)

View File

@@ -23,6 +23,7 @@ import aiofiles
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -109,6 +110,10 @@ BTMINER_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -655,6 +660,36 @@ class BTMiner(StockFirmware):
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data
async def upgrade_firmware(self, file: Path):
"""
Upgrade the firmware of the Whatsminer device.

View File

@@ -14,15 +14,15 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import Optional, List
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, HashUnit
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
from pyasic.rpc.cgminer import CGMinerRPCAPI
from pyasic.data.pools import PoolMetrics, PoolUrl
CGMINER_DATA_LOC = DataLocations(
**{

View File

@@ -454,8 +454,9 @@ class ePIC(ePICFirmware):
except LookupError:
pass
async def upgrade_firmware(self, file: Path | str, keep_settings: bool = True) -> bool:
async def upgrade_firmware(
self, file: Path | str, keep_settings: bool = True
) -> bool:
"""
Upgrade the firmware of the ePIC miner device.
@@ -466,4 +467,4 @@ class ePIC(ePICFirmware):
Returns:
bool: Whether the firmware update succeeded.
"""
return await self.web.system_update(file=file, keep_settings=keep_settings)
return await self.web.system_update(file=file, keep_settings=keep_settings)

View File

@@ -62,6 +62,10 @@ GOLDSHELL_DATA_LOC = DataLocations(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)

View File

@@ -1,6 +1,8 @@
from typing import List, Optional
from pyasic import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device import MinerAlgo
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
@@ -41,6 +43,10 @@ ICERIVER_DATA_LOC = DataLocations(
"_get_uptime",
[WebAPICommand("web_userpanel", "userpanel")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("web_userpanel", "userpanel")],
),
}
)
@@ -67,6 +73,11 @@ class IceRiver(StockFirmware):
return False
return True
async def get_config(self) -> MinerConfig:
web_userpanel = await self.web.userpanel()
return MinerConfig.from_iceriver(web_userpanel)
async def _get_fans(self, web_userpanel: dict = None) -> List[Fan]:
if web_userpanel is None:
try:
@@ -76,7 +87,7 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
return [Fan(spd) for spd in web_userpanel["fans"]]
return [Fan(spd) for spd in web_userpanel["userpanel"]["data"]["fans"]]
except (LookupError, ValueError, TypeError):
pass
@@ -89,7 +100,9 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
return web_userpanel["mac"].upper().replace("-", ":")
return (
web_userpanel["userpanel"]["data"]["mac"].upper().replace("-", ":")
)
except (LookupError, ValueError, TypeError):
pass
@@ -102,7 +115,7 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
return web_userpanel["host"]
return web_userpanel["userpanel"]["data"]["host"]
except (LookupError, ValueError, TypeError):
pass
@@ -115,9 +128,13 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
base_unit = web_userpanel["unit"]
base_unit = web_userpanel["userpanel"]["data"]["unit"]
return AlgoHashRate.SHA256(
float(web_userpanel["rtpow"].replace(base_unit, "")),
float(
web_userpanel["userpanel"]["data"]["rtpow"].replace(
base_unit, ""
)
),
unit=MinerAlgo.SHA256.unit.from_str(base_unit + "H"),
).into(MinerAlgo.SHA256.unit.default)
except (LookupError, ValueError, TypeError):
@@ -132,7 +149,7 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
return web_userpanel["locate"]
return web_userpanel["userpanel"]["data"]["locate"]
except (LookupError, ValueError, TypeError):
pass
return False
@@ -146,7 +163,7 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
return web_userpanel["powstate"]
return web_userpanel["userpanel"]["data"]["powstate"]
except (LookupError, ValueError, TypeError):
pass
@@ -164,14 +181,14 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
for board in web_userpanel["boards"]:
idx = board["no"] - 1
for board in web_userpanel["userpanel"]["data"]["boards"]:
idx = int(board["no"] - 1)
hb_list[idx].chip_temp = round(board["outtmp"])
hb_list[idx].temp = round(board["intmp"])
hb_list[idx].hashrate = AlgoHashRate.SHA256(
float(board["rtpow"].replace("G", "")), HashUnit.SHA256.GH
).into(self.algo.unit.default)
hb_list[idx].chips = board["chipnum"]
hb_list[idx].chips = int(board["chipnum"])
hb_list[idx].missing = False
except LookupError:
pass
@@ -186,7 +203,7 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
runtime = web_userpanel["runtime"]
runtime = web_userpanel["userpanel"]["data"]["runtime"]
days, hours, minutes, seconds = runtime.split(":")
return (
(int(days) * 24 * 60 * 60)
@@ -196,3 +213,36 @@ class IceRiver(StockFirmware):
)
except (LookupError, ValueError, TypeError):
pass
async def _get_pools(self, web_userpanel: dict = None) -> List[PoolMetrics]:
if web_userpanel is None:
try:
web_userpanel = await self.web.userpanel()
except APIError:
pass
pools_data = []
if web_userpanel is not None:
try:
pools = web_userpanel["userpanel"]["data"]["pools"]
for pool_info in pools:
pool_num = pool_info.get("no")
if pool_num is not None:
pool_num = int(pool_num)
if pool_info["addr"] == "":
continue
url = pool_info.get("addr")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("accepted"),
rejected=pool_info.get("rejected"),
active=pool_info.get("connect"),
alive=int(pool_info.get("state", 0)) == 1,
url=pool_url,
user=pool_info.get("user"),
index=pool_num,
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -19,6 +19,7 @@ from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.error_codes.innosilicon import InnosiliconError
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.data import (
@@ -29,7 +30,6 @@ from pyasic.miners.data import (
WebAPICommand,
)
from pyasic.web.innosilicon import InnosiliconWebAPI
from pyasic.data.pools import PoolMetrics, PoolUrl
INNOSILICON_DATA_LOC = DataLocations(
**{
@@ -92,9 +92,8 @@ INNOSILICON_DATA_LOC = DataLocations(
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")]
)
"_get_pools", [RPCAPICommand("rpc_pools", "pools")]
),
}
)
@@ -116,7 +115,7 @@ class Innosilicon(CGMiner):
except APIError:
return self.config
self.config = MinerConfig.from_inno([pools])
self.config = MinerConfig.from_inno(pools["pools"])
return self.config
async def reboot(self) -> bool:

View File

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import logging
from typing import List, Optional
from pyasic.config import MinerConfig
@@ -55,6 +56,15 @@ LUXMINER_DATA_LOC = DataLocations(
str(DataOptions.POOLS): DataFunction(
"_get_pools", [RPCAPICommand("rpc_pools", "pools")]
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver", [RPCAPICommand("rpc_version", "version")]
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver", [RPCAPICommand("rpc_version", "version")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light", [RPCAPICommand("rpc_config", "config")]
),
}
)
@@ -67,25 +77,9 @@ class LUXMiner(LuxOSFirmware):
data_locations = LUXMINER_DATA_LOC
async def _get_session(self) -> Optional[str]:
try:
data = await self.rpc.session()
if not data["SESSION"][0]["SessionID"] == "":
return data["SESSION"][0]["SessionID"]
except APIError:
pass
try:
data = await self.rpc.logon()
return data["SESSION"][0]["SessionID"]
except (LookupError, APIError):
return
async def fault_light_on(self) -> bool:
try:
session_id = await self._get_session()
if session_id:
await self.rpc.ledset(session_id, "red", "blink")
await self.rpc.ledset("red", "blink")
return True
except (APIError, LookupError):
pass
@@ -93,9 +87,7 @@ class LUXMiner(LuxOSFirmware):
async def fault_light_off(self) -> bool:
try:
session_id = await self._get_session()
if session_id:
await self.rpc.ledset(session_id, "red", "off")
await self.rpc.ledset("red", "off")
return True
except (APIError, LookupError):
pass
@@ -106,9 +98,7 @@ class LUXMiner(LuxOSFirmware):
async def restart_luxminer(self) -> bool:
try:
session_id = await self._get_session()
if session_id:
await self.rpc.resetminer(session_id)
await self.rpc.resetminer()
return True
except (APIError, LookupError):
pass
@@ -116,9 +106,7 @@ class LUXMiner(LuxOSFirmware):
async def stop_mining(self) -> bool:
try:
session_id = await self._get_session()
if session_id:
await self.rpc.curtail(session_id)
await self.rpc.sleep()
return True
except (APIError, LookupError):
pass
@@ -126,45 +114,60 @@ class LUXMiner(LuxOSFirmware):
async def resume_mining(self) -> bool:
try:
session_id = await self._get_session()
if session_id:
await self.rpc.wakeup(session_id)
await self.rpc.wakeup()
return True
except (APIError, LookupError):
pass
async def reboot(self) -> bool:
try:
session_id = await self._get_session()
if session_id:
await self.rpc.rebootdevice(session_id)
await self.rpc.rebootdevice()
return True
except (APIError, LookupError):
pass
return False
async def get_config(self) -> MinerConfig:
return self.config
data = await self.rpc.multicommand("tempctrl", "fans", "pools", "groups")
return MinerConfig.from_luxos(
rpc_tempctrl=data.get("tempctrl", [{}])[0],
rpc_fans=data.get("fans", [{}])[0],
rpc_pools=data.get("pools", [{}])[0],
rpc_groups=data.get("groups", [{}])[0],
)
async def upgrade_firmware(self) -> bool:
"""
Upgrade the firmware on a LuxOS miner by calling the 'updaterun' API command.
Returns:
bool: True if the firmware upgrade was successfully initiated, False otherwise.
"""
try:
await self.rpc.upgraderun()
logging.info(f"{self.ip}: Firmware upgrade initiated successfully.")
return True
except APIError as e:
logging.error(f"{self.ip}: Firmware upgrade failed: {e}")
return False
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
async def _get_mac(self, rpc_config: dict = None) -> Optional[str]:
mac = None
if rpc_config is None:
try:
rpc_config = await self.rpc.config()
except APIError:
return None
pass
if rpc_config is not None:
try:
mac = rpc_config["CONFIG"][0]["MACAddr"]
return rpc_config["CONFIG"][0]["MACAddr"].upper()
except KeyError:
return None
return mac
pass
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
@@ -182,59 +185,47 @@ class LUXMiner(LuxOSFirmware):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = []
hashboards = [
HashBoard(idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
board_offset = -1
boards = rpc_stats["STATS"]
if len(boards) > 1:
for board_num in range(1, 16, 5):
for _b_num in range(5):
b = boards[1].get(f"chain_acn{board_num + _b_num}")
if b and not b == 0 and board_offset == -1:
board_offset = board_num
if board_offset == -1:
board_offset = 1
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
# TODO: bugged on S9 because of index issues, fix later.
board_stats = rpc_stats["STATS"][1]
for idx in range(3):
board_n = idx + 1
hashboards[idx].hashrate = AlgoHashRate.SHA256(
float(board_stats[f"chain_rate{board_n}"]), HashUnit.SHA256.GH
).into(self.algo.unit.default)
hashboards[idx].chips = int(board_stats[f"chain_acn{board_n}"])
chip_temp_data = list(
filter(
lambda x: not x == 0,
map(int, board_stats[f"temp_chip{board_n}"].split("-")),
)
chip_temp = boards[1].get(f"temp{i}")
if chip_temp:
hashboard.chip_temp = round(chip_temp)
temp = boards[1].get(f"temp2_{i}")
if temp:
hashboard.temp = round(temp)
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.GH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
except (LookupError, ValueError, TypeError):
)
hashboards[idx].chip_temp = (
sum([chip_temp_data[0], chip_temp_data[3]]) / 2
)
board_temp_data = list(
filter(
lambda x: not x == 0,
map(int, board_stats[f"temp_pcb{board_n}"].split("-")),
)
)
hashboards[idx].temp = (
sum([board_temp_data[1], board_temp_data[2]]) / 2
)
hashboards[idx].missing = False
except LookupError:
pass
return hashboards
async def _get_wattage(self, rpc_power: dict = None) -> Optional[int]:
@@ -302,6 +293,45 @@ class LUXMiner(LuxOSFirmware):
except LookupError:
pass
async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None:
try:
rpc_version = await self.rpc.version()
except APIError:
pass
if rpc_version is not None:
try:
return rpc_version["VERSION"][0]["Miner"]
except LookupError:
pass
async def _get_api_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None:
try:
rpc_version = await self.rpc.version()
except APIError:
pass
if rpc_version is not None:
try:
return rpc_version["VERSION"][0]["API"]
except LookupError:
pass
async def _get_fault_light(self, rpc_config: dict = None) -> Optional[bool]:
if rpc_config is None:
try:
rpc_config = await self.rpc.config()
except APIError:
pass
if rpc_config is not None:
try:
return not rpc_config["CONFIG"][0]["RedLed"] == "off"
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:

View File

@@ -3,6 +3,7 @@ from typing import List, Optional
from pyasic import MinerConfig
from pyasic.config import MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.miners.device.firmware import MaraFirmware
@@ -60,6 +61,10 @@ MARA_DATA_LOC = DataLocations(
"_get_uptime",
[WebAPICommand("web_brief", "brief")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("web_pools", "pools")],
),
}
)
@@ -305,3 +310,43 @@ class MaraMiner(MaraFirmware):
return web_miner_config["mode"]["concorde"]["power-target"]
except LookupError:
pass
async def _get_pools(self, web_pools: list = None) -> List[PoolMetrics]:
if web_pools is None:
try:
web_pools = await self.web.pools()
except APIError:
return []
active_pool_index = None
highest_priority = float("inf")
for pool_info in web_pools:
if (
pool_info.get("status") == "Alive"
and pool_info.get("priority", float("inf")) < highest_priority
):
highest_priority = pool_info["priority"]
active_pool_index = pool_info["index"]
pools_data = []
if web_pools is not None:
try:
for pool_info in web_pools:
url = pool_info.get("url")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("accepted"),
rejected=pool_info.get("rejected"),
get_failures=pool_info.get("stale"),
remote_failures=pool_info.get("discarded"),
active=pool_info.get("index") == active_pool_index,
alive=pool_info.get("status") == "Alive",
url=pool_url,
user=pool_info.get("user"),
index=pool_info.get("index"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -237,13 +237,17 @@ class VNish(VNishFirmware, BMMiner):
async def _is_mining(self, web_summary: dict = None) -> Optional[bool]:
if web_summary is None:
web_summary = await self.web.summary()
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
is_mining = not web_summary["miner"]["miner_status"]["miner_state"] in [
"stopped",
"shutting-down",
"failure",
]
return is_mining
except LookupError:

View File

@@ -560,7 +560,14 @@ class BaseMiner(MinerProtocol):
if self._ssh_cls is not None:
self.ssh = self._ssh_cls(ip)
async def upgrade_firmware(self, *, file: str = None, url: str = None, version: str = None, keep_settings: bool = True) -> bool:
async def upgrade_firmware(
self,
*,
file: str = None,
url: str = None,
version: str = None,
keep_settings: bool = True,
) -> bool:
"""Upgrade the firmware of the miner.
Parameters:
@@ -574,4 +581,5 @@ class BaseMiner(MinerProtocol):
"""
return False
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)

View File

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

View File

@@ -43,4 +43,4 @@ class LuxOSFirmware(BaseMiner):
class MaraFirmware(BaseMiner):
firmware = MinerFirmware.MARATHON
firmware = MinerFirmware.MARATHON

View File

@@ -51,4 +51,4 @@ class BitAxeMake(BaseMiner):
class IceRiverMake(BaseMiner):
make = MinerMake.BITAXE
make = MinerMake.ICERIVER

View File

@@ -21,3 +21,9 @@ class Z15(AntMinerMake):
raw_model = MinerModel.ANTMINER.Z15
expected_chips = 3
class Z15Pro(AntMinerMake):
raw_model = MinerModel.ANTMINER.Z15Pro
expected_chips = 6

View File

@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .Z15 import Z15
from .Z15 import Z15, Z15Pro

View File

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

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.device.models import MinerModel
from pyasic.miners.device.makes import AntMinerMake
class KS3(AntMinerMake):
raw_model = MinerModel.ANTMINER.KS3
expected_chips = 92
expected_fans = 2

View File

@@ -15,4 +15,6 @@
# ------------------------------------------------------------------------------
from .D3 import D3
from .HS3 import HS3
from .KA3 import KA3
from .KS3 import KS3
from .L3 import L3Plus

View File

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

View File

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

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.device.models import MinerModel
from pyasic.miners.device.makes import AntMinerMake
class K7(AntMinerMake):
raw_model = MinerModel.ANTMINER.K7
expected_chips = 92
expected_fans = 4

View File

@@ -13,4 +13,5 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .K7 import K7
from .L7 import L7

View File

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

View File

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

View File

@@ -21,3 +21,4 @@ class KS2(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS2
expected_fans = 4
expected_chips = 18

View File

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

View File

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

View File

@@ -1 +1,5 @@
from .KS0 import KS0
from .KS1 import KS1
from .KS2 import KS2
from .KS3 import KS3, KS3L, KS3M
from .KS5 import KS5, KS5L, KS5M

View File

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

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.device.models import MinerModel
from pyasic.miners.device.makes import InnosiliconMake
class A11MX(InnosiliconMake):
raw_model = MinerModel.INNOSILICON.A11MX
expected_hashboards = 4
expected_chips = 8

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 .A11 import *
from .A11M import *

View File

@@ -15,4 +15,5 @@
# ------------------------------------------------------------------------------
from .A10X import *
from .A11X import *
from .T3X import *

View File

@@ -69,6 +69,8 @@ class M50VH60(WhatsMinerMake):
class M50VH70(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50VH70
expected_chips = 105
class M50VH80(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50VH80
@@ -76,6 +78,12 @@ class M50VH80(WhatsMinerMake):
expected_chips = 111
class M50VH90(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50VH90
expected_chips = 117
class M50VJ10(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50VJ10
@@ -83,6 +91,10 @@ class M50VJ10(WhatsMinerMake):
class M50VJ20(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50VJ20
expected_chips = 111
class M50VJ30(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50VJ30
expected_chips = 117

View File

@@ -51,3 +51,5 @@ class M50SVH40(WhatsMinerMake):
class M50SVH50(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M50SVH50
expected_chips = 135

View File

@@ -25,6 +25,7 @@ from .M50 import (
M50VH60,
M50VH70,
M50VH80,
M50VH90,
M50VJ10,
M50VJ20,
M50VJ30,

View File

@@ -24,6 +24,8 @@ class M60VK10(WhatsMinerMake):
class M60VK20(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M60VK20
expected_chips = 172
class M60VK30(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M60VK30

View File

@@ -20,6 +20,7 @@ import enum
import ipaddress
import json
import re
import warnings
from typing import Any, AsyncGenerator, Callable
import anyio
@@ -66,14 +67,19 @@ MINER_CLASSES = {
"ANTMINER D3": CGMinerD3,
"ANTMINER HS3": BMMinerHS3,
"ANTMINER L3+": BMMinerL3Plus,
"ANTMINER KA3": BMMinerKA3,
"ANTMINER KS3": BMMinerKS3,
"ANTMINER DR5": CGMinerDR5,
"ANTMINER KS5": BMMinerKS5,
"ANTMINER L7": BMMinerL7,
"ANTMINER K7": BMMinerK7,
"ANTMINER E9 PRO": BMMinerE9Pro,
"ANTMINER S9": BMMinerS9,
"ANTMINER S9I": BMMinerS9i,
"ANTMINER S9J": BMMinerS9j,
"ANTMINER T9": BMMinerT9,
"ANTMINER Z15": CGMinerZ15,
"ANTMINER Z15 PRO": BMMinerZ15Pro,
"ANTMINER S17": BMMinerS17,
"ANTMINER S17+": BMMinerS17Plus,
"ANTMINER S17 PRO": BMMinerS17Pro,
@@ -271,6 +277,7 @@ MINER_CLASSES = {
"M50VH60": BTMinerM50VH60,
"M50VH70": BTMinerM50VH70,
"M50VH80": BTMinerM50VH80,
"M50VH90": BTMinerM50VH90,
"M50VJ10": BTMinerM50VJ10,
"M50VJ20": BTMinerM50VJ20,
"M50VJ30": BTMinerM50VJ30,
@@ -338,6 +345,8 @@ MINER_CLASSES = {
None: type("InnosiliconUnknown", (Innosilicon, InnosiliconMake), {}),
"T3H+": InnosiliconT3HPlus,
"A10X": InnosiliconA10X,
"A11": InnosiliconA11,
"A11MX": InnosiliconA11MX,
},
MinerTypes.GOLDSHELL: {
None: type("GoldshellUnknown", (GoldshellMiner, GoldshellMake), {}),
@@ -376,6 +385,7 @@ MINER_CLASSES = {
"ANTMINER S19 PRO+ HYD.": BOSMinerS19ProPlusHydro,
"ANTMINER T19": BOSMinerT19,
"ANTMINER S21": BOSMinerS21,
"ANTMINER T21": BOSMinerT21,
},
MinerTypes.VNISH: {
None: VNish,
@@ -389,6 +399,7 @@ MINER_CLASSES = {
"ANTMINER S19 PRO": VNishS19Pro,
"ANTMINER S19J": VNishS19j,
"ANTMINER S19J PRO": VNishS19jPro,
"ANTMINER S19J PRO BB": VNishS19jPro,
"ANTMINER S19A": VNishS19a,
"ANTMINER S19A PRO": VNishS19aPro,
"ANTMINER S19 PRO HYD.": VNishS19ProHydro,
@@ -456,7 +467,15 @@ MINER_CLASSES = {
},
MinerTypes.ICERIVER: {
None: type("IceRiverUnknown", (IceRiver, IceRiverMake), {}),
"KS0": IceRiverKS0,
"KS1": IceRiverKS1,
"KS2": IceRiverKS2,
"KS3": IceRiverKS3,
"KS3L": IceRiverKS3L,
"KS3M": IceRiverKS3M,
"KS5": IceRiverKS5,
"KS5L": IceRiverKS5L,
"KS5M": IceRiverKS5M,
},
}
@@ -535,6 +554,7 @@ class MinerFactory:
MinerTypes.AURADINE: self.get_miner_model_auradine,
MinerTypes.MARATHON: self.get_miner_model_marathon,
MinerTypes.BITAXE: self.get_miner_model_bitaxe,
MinerTypes.ICERIVER: self.get_miner_model_iceriver,
}
fn = miner_model_fns.get(miner_type)
@@ -616,6 +636,10 @@ class MinerFactory:
return MinerTypes.WHATSMINER
if "Braiins OS" in web_text:
return MinerTypes.BRAIINS_OS
if "Luxor Firmware" in web_text:
return MinerTypes.LUX_OS
if "<TITLE>用户界面</TITLE>" in web_text:
return MinerTypes.ICERIVER
if "AxeOS" in web_text:
return MinerTypes.BITAXE
if "cloud-box" in web_text:
@@ -630,8 +654,6 @@ class MinerFactory:
return MinerTypes.INNOSILICON
if "Miner UI" in web_text:
return MinerTypes.AURADINE
if "<TITLE>用户界面</TITLE>" in web_text:
return MinerTypes.ICERIVER
async def _get_miner_socket(self, ip: str) -> MinerTypes | None:
commands = ["version", "devdetails"]
@@ -698,10 +720,10 @@ class MinerFactory:
return MinerTypes.BRAIINS_OS
if "BTMINER" in upper_data or "BITMICRO" in upper_data:
return MinerTypes.WHATSMINER
if "HIVEON" in upper_data:
return MinerTypes.HIVEON
if "LUXMINER" in upper_data:
return MinerTypes.LUX_OS
if "HIVEON" in upper_data:
return MinerTypes.HIVEON
if "KAONSU" in upper_data:
return MinerTypes.MARATHON
if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data:
@@ -712,11 +734,13 @@ class MinerFactory:
or "BFGMINER" in upper_data
):
return MinerTypes.GOLDSHELL
if "INNOMINER" in upper_data:
return MinerTypes.INNOSILICON
if "AVALON" in upper_data:
return MinerTypes.AVALONMINER
if "GCMINER" in upper_data or "FLUXOS" in upper_data:
return MinerTypes.AURADINE
if "VNISH" in upper_data or "DEVICE PATH" in upper_data:
if "VNISH" in upper_data:
return MinerTypes.VNISH
async def send_web_command(
@@ -804,10 +828,8 @@ class MinerFactory:
# fix an error with a btminer return having a missing comma. (2023-01-06 version)
str_data = str_data.replace('""temp0', '","temp0')
# fix an error with Avalonminers returning inf and nan
str_data = str_data.replace("info", "1nfo")
str_data = str_data.replace("inf", "0")
str_data = str_data.replace("1nfo", "info")
str_data = str_data.replace("nan", "0")
str_data = str_data.replace('"inf"', "0")
str_data = str_data.replace('"nan"', "0")
# fix whatever this garbage from avalonminers is `,"id":1}`
if str_data.startswith(","):
str_data = f"{{{str_data[1:]}"
@@ -832,6 +854,10 @@ class MinerFactory:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
except LookupError:
if miner_type in MINER_CLASSES:
warnings.warn(
f"Partially supported miner found: {miner_model}, please open an issue with miner data "
f"and this model on GitHub (https://github.com/UpstreamData/pyasic/issues)."
)
return MINER_CLASSES[miner_type][None](ip)
return UnknownMiner(str(ip))
@@ -910,7 +936,7 @@ class MinerFactory:
miner_model = sock_json_data["VERSION"][0]["PROD"].upper()
if "-" in miner_model:
miner_model = miner_model.split("-")[0]
if miner_model == "AVALONNANO":
if miner_model in ["AVALONNANO", "AVALON0O"]:
nano_subtype = sock_json_data["VERSION"][0]["MODEL"].upper()
miner_model = f"AVALONMINER {nano_subtype}"
return miner_model
@@ -922,10 +948,19 @@ class MinerFactory:
async with httpx.AsyncClient(transport=settings.transport()) as session:
auth_req = await session.post(
f"http://{ip}/api/auth",
data={"username": "admin", "password": "admin"},
data={
"username": "admin",
"password": settings.get(
"default_innosilicon_web_password", "admin"
),
},
)
auth = auth_req.json()["jwt"]
except (httpx.HTTPError, LookupError):
return
try:
async with httpx.AsyncClient(transport=settings.transport()) as session:
web_data = (
await session.post(
f"http://{ip}/api/type",
@@ -936,6 +971,18 @@ class MinerFactory:
return web_data["type"]
except (httpx.HTTPError, LookupError):
pass
try:
async with httpx.AsyncClient(transport=settings.transport()) as session:
web_data = (
await session.post(
f"http://{ip}/overview",
headers={"Authorization": "Bearer " + auth},
data={},
)
).json()
return web_data["type"]
except (httpx.HTTPError, LookupError):
pass
async def get_miner_model_braiins_os(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "devdetails")
@@ -1049,6 +1096,39 @@ class MinerFactory:
except (TypeError, LookupError):
pass
async def get_miner_model_iceriver(self, ip: str) -> str | None:
async with httpx.AsyncClient(transport=settings.transport()) as client:
try:
# auth
await client.post(
f"http://{ip}/user/loginpost",
params={
"post": "6",
"user": "admin",
"pwd": settings.get(
"default_iceriver_web_password", "12345678"
),
},
)
except httpx.HTTPError:
return None
try:
resp = await client.post(
f"http://{ip}:/user/userpanel", params={"post": "4"}
)
if not resp.status_code == 200:
return
result = resp.json()
software_ver = result["data"]["softver1"]
split_ver = software_ver.split("_")
if split_ver[-1] == "miner":
miner_ver = split_ver[-2]
else:
miner_ver = split_ver[-1].replace("miner", "")
return miner_ver.upper()
except httpx.HTTPError:
pass
miner_factory = MinerFactory()

View File

@@ -0,0 +1,6 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models import KS0
class IceRiverKS0(IceRiver, KS0):
pass

View File

@@ -0,0 +1,6 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models import KS1
class IceRiverKS1(IceRiver, KS1):
pass

View File

@@ -0,0 +1,14 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models.iceriver import KS3, KS3L, KS3M
class IceRiverKS3(IceRiver, KS3):
pass
class IceRiverKS3L(IceRiver, KS3L):
pass
class IceRiverKS3M(IceRiver, KS3M):
pass

View File

@@ -0,0 +1,14 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models.iceriver import KS5, KS5L, KS5M
class IceRiverKS5(IceRiver, KS5):
pass
class IceRiverKS5L(IceRiver, KS5L):
pass
class IceRiverKS5M(IceRiver, KS5M):
pass

View File

@@ -1 +1,5 @@
from .KS0 import IceRiverKS0
from .KS1 import IceRiverKS1
from .KS2 import IceRiverKS2
from .KS3 import IceRiverKS3, IceRiverKS3L, IceRiverKS3M
from .KS5 import IceRiverKS5, IceRiverKS5L, IceRiverKS5M

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.innosilicon import Innosilicon
from pyasic.miners.device.models import A11
class InnosiliconA11(Innosilicon, A11):
pass

View File

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

View File

@@ -0,0 +1,2 @@
from .A11 import InnosiliconA11
from .A11M import InnosiliconA11MX

View File

@@ -15,4 +15,5 @@
# ------------------------------------------------------------------------------
from .A10X import *
from .A11X import *
from .T3X import *

View File

@@ -26,6 +26,7 @@ from pyasic.miners.device.models import (
M50VH60,
M50VH70,
M50VH80,
M50VH90,
M50VJ10,
M50VJ20,
M50VJ30,
@@ -72,6 +73,10 @@ class BTMinerM50VH80(M5X, M50VH80):
pass
class BTMinerM50VH90(M5X, M50VH90):
pass
class BTMinerM50VJ10(M5X, M50VJ10):
pass

View File

@@ -25,6 +25,7 @@ from .M50 import (
BTMinerM50VH60,
BTMinerM50VH70,
BTMinerM50VH80,
BTMinerM50VH90,
BTMinerM50VJ10,
BTMinerM50VJ20,
BTMinerM50VJ30,

View File

@@ -268,10 +268,9 @@ If you are sure you want to use this command please use API.send_command("{comma
# fix an error with a btminer return having a missing comma. (2023-01-06 version)
str_data = str_data.replace('""temp0', '","temp0')
# fix an error with Avalonminers returning inf and nan
str_data = str_data.replace("info", "1nfo")
str_data = str_data.replace("inf", "0")
str_data = str_data.replace("1nfo", "info")
str_data = str_data.replace("nan", "0")
str_data = str_data.replace('"inf"', "0")
str_data = str_data.replace('"nan"', "0")
# fix whatever this garbage from avalonminers is `,"id":1}`
if str_data.startswith(","):
str_data = f"{{{str_data[1:]}"

View File

@@ -22,8 +22,8 @@ import hashlib
import json
import logging
import re
from typing import Literal, Union
import struct
from typing import Literal, Union
import httpx
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

View File

@@ -13,8 +13,9 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import Literal
from typing import Literal, Optional, Union
from pyasic import APIError
from pyasic.rpc.base import BaseMinerRPCAPI
@@ -32,6 +33,48 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
rely on it to send the command for them.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.session_token = None
async def send_privileged_command(
self, command: Union[str, bytes], *args, **kwargs
) -> dict:
if self.session_token is None:
await self.auth()
return await self.send_command(
command,
self.session_token,
*args,
**kwargs,
)
async def send_command(
self,
command: Union[str, bytes],
*args,
**kwargs,
) -> dict:
if kwargs.get("parameters") is not None and len(args) == 0:
return await super().send_command(command, **kwargs)
return await super().send_command(command, parameters=",".join(args), **kwargs)
async def auth(self) -> Optional[str]:
try:
data = await self.session()
if not data["SESSION"][0]["SessionID"] == "":
self.session_token = data["SESSION"][0]["SessionID"]
return self.session_token
except APIError:
pass
try:
data = await self.logon()
self.session_token = data["SESSION"][0]["SessionID"]
return self.session_token
except (LookupError, APIError):
pass
async def addgroup(self, name: str, quota: int) -> dict:
"""Add a pool group.
<details>
@@ -45,7 +88,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Confirmation of adding a pool group.
</details>
"""
return await self.send_command("addgroup", parameters=f"{name},{quota}")
return await self.send_command("addgroup", name, quota)
async def addpool(
self, url: str, user: str, pwd: str = "", group_id: str = None
@@ -67,7 +110,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
pool_data = [url, user, pwd]
if group_id is not None:
pool_data.append(group_id)
return await self.send_command("addpool", parameters=",".join(pool_data))
return await self.send_command("addpool", *pool_data)
async def asc(self, n: int) -> dict:
"""Get data for ASC device n.
@@ -81,7 +124,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
The data for ASC device n.
</details>
"""
return await self.send_command("asc", parameters=n)
return await self.send_command("asc", n)
async def asccount(self) -> dict:
"""Get data on the number of ASC devices and their info.
@@ -108,7 +151,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
* Access (Y/N) <- you have access to use the command
</details>
"""
return await self.send_command("check", parameters=command)
return await self.send_command("check", command)
async def coin(self) -> dict:
"""Get information on the current coin.
@@ -137,19 +180,38 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("config")
async def curtail(self, session_id: str) -> dict:
"""Put the miner into sleep mode. Requires a session_id from logon.
async def curtail(self) -> dict:
"""Put the miner into sleep mode.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns:
A confirmation of putting the miner to sleep.
</details>
"""
return await self.send_command("curtail", parameters=session_id)
return await self.send_privileged_command("curtail", "sleep")
async def sleep(self) -> dict:
"""Put the miner into sleep mode.
<details>
<summary>Expand</summary>
Returns:
A confirmation of putting the miner to sleep.
</details>
"""
return await self.send_privileged_command("curtail", "sleep")
async def wakeup(self) -> dict:
"""Wake the miner up from sleep mode.
<details>
<summary>Expand</summary>
Returns:
A confirmation of waking the miner up.
</details>
"""
return await self.send_privileged_command("curtail", "wakeup")
async def devdetails(self) -> dict:
"""Get data on all devices with their static details.
@@ -185,7 +247,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of diabling the pool.
</details>
"""
return await self.send_command("disablepool", parameters=n)
return await self.send_command("disablepool", n)
async def edevs(self) -> dict:
"""Alias for devs"""
@@ -203,7 +265,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of enabling pool n.
</details>
"""
return await self.send_command("enablepool", parameters=n)
return await self.send_command("enablepool", n)
async def estats(self) -> dict:
"""Alias for stats"""
@@ -220,13 +282,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("fans")
async def fanset(self, session_id: str, speed: int, min_fans: int = None) -> dict:
"""Set fan control. Requires a session_id from logon.
async def fanset(
self, speed: int = None, min_fans: int = None, power_off_speed: int = None
) -> dict:
"""Set fan control.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
speed: The fan speed to set. Use -1 to set automatically.
min_fans: The minimum number of fans to use. Optional.
@@ -234,10 +297,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting fan control values.
</details>
"""
fanset_data = [str(session_id), str(speed)]
fanset_data = []
if speed is not None:
fanset_data.append(f"speed={speed}")
if min_fans is not None:
fanset_data.append(str(min_fans))
return await self.send_command("fanset", parameters=",".join(fanset_data))
fanset_data.append(f"min_fans={min_fans}")
if power_off_speed is not None:
fanset_data.append(f"power_off_speed={power_off_speed}")
return await self.send_privileged_command("fanset", *fanset_data)
async def frequencyget(self, board_n: int, chip_n: int = None) -> dict:
"""Get frequency data for a board and chips.
@@ -255,17 +322,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
frequencyget_data = [str(board_n)]
if chip_n is not None:
frequencyget_data.append(str(chip_n))
return await self.send_command(
"frequencyget", parameters=",".join(frequencyget_data)
)
return await self.send_command("frequencyget", *frequencyget_data)
async def frequencyset(self, session_id: str, board_n: int, freq: int) -> dict:
"""Set frequency. Requires a session_id from logon.
async def frequencyset(self, board_n: int, freq: int) -> dict:
"""Set frequency.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
board_n: The board number to set frequency on.
freq: The frequency to set.
@@ -273,26 +337,21 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting frequency values.
</details>
"""
return await self.send_command(
"frequencyset", parameters=f"{session_id},{board_n},{freq}"
)
return await self.send_privileged_command("frequencyset", board_n, freq)
async def frequencystop(self, session_id: str, board_n: int) -> dict:
"""Stop set frequency. Requires a session_id from logon.
async def frequencystop(self, board_n: int) -> dict:
"""Stop set frequency.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
board_n: The board number to set frequency on.
Returns:
A confirmation of stopping frequencyset value.
</details>
"""
return await self.send_command(
"frequencystop", parameters=f"{session_id},{board_n}"
)
return await self.send_privileged_command("frequencystop", board_n)
async def groupquota(self, group_n: int, quota: int) -> dict:
"""Set a group's quota.
@@ -307,7 +366,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting quota value.
</details>
"""
return await self.send_command("groupquota", parameters=f"{group_n},{quota}")
return await self.send_command("groupquota", group_n, quota)
async def groups(self) -> dict:
"""Get pool group data.
@@ -336,19 +395,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
healthchipget_data = [str(board_n)]
if chip_n is not None:
healthchipget_data.append(str(chip_n))
return await self.send_command(
"healthchipget", parameters=",".join(healthchipget_data)
)
return await self.send_command("healthchipget", *healthchipget_data)
async def healthchipset(
self, session_id: str, board_n: int, chip_n: int = None
) -> dict:
"""Select the next chip to have its health checked. Requires a session_id from logon.
async def healthchipset(self, board_n: int, chip_n: int = None) -> dict:
"""Select the next chip to have its health checked.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
board_n: The board number to next get chip health of.
chip_n: The chip number to next get chip health of. Optional.
@@ -356,12 +410,10 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Confirmation of selecting the next health check chip.
</details>
"""
healthchipset_data = [session_id, str(board_n)]
healthchipset_data = [str(board_n)]
if chip_n is not None:
healthchipset_data.append(str(chip_n))
return await self.send_command(
"healthchipset", parameters=",".join(healthchipset_data)
)
return await self.send_privileged_command("healthchipset", *healthchipset_data)
async def healthctrl(self) -> dict:
"""Get health check config.
@@ -374,15 +426,12 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("healthctrl")
async def healthctrlset(
self, session_id: str, num_readings: int, amplified_factor: float
) -> dict:
"""Set health control config. Requires a session_id from logon.
async def healthctrlset(self, num_readings: int, amplified_factor: float) -> dict:
"""Set health control config.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
num_readings: The minimum number of readings for evaluation.
amplified_factor: Performance factor of the evaluation.
@@ -390,9 +439,8 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting health control config.
</details>
"""
return await self.send_command(
"healthctrlset",
parameters=f"{session_id},{num_readings},{amplified_factor}",
return await self.send_privileged_command(
"healthctrlset", num_readings, amplified_factor
)
async def kill(self) -> dict:
@@ -419,16 +467,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
async def ledset(
self,
session_id: str,
color: Literal["red"],
state: Literal["on", "off", "blink"],
) -> dict:
"""Set led. Requires a session_id from logon.
"""Set led.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
color: The color LED to set. Can be "red".
state: The state to set the LED to. Can be "on", "off", or "blink".
@@ -436,9 +482,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting LED.
</details>
"""
return await self.send_command(
"ledset", parameters=f"{session_id},{color},{state}"
)
return await self.send_privileged_command("ledset", color, state)
async def limits(self) -> dict:
"""Get max and min values of config parameters.
@@ -451,8 +495,8 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("limits")
async def logoff(self, session_id: str) -> dict:
"""Log off of a session. Requires a session id from an active session.
async def logoff(self) -> dict:
"""Log off of a session.
<details>
<summary>Expand</summary>
@@ -463,7 +507,9 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Confirmation of logging off a session.
</details>
"""
return await self.send_command("logoff", parameters=session_id)
res = await self.send_privileged_command("logoff")
self.session_token = None
return res
async def logon(self) -> dict:
"""Get or create a session.
@@ -510,13 +556,12 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("profiles")
async def profileset(self, session_id: str, board_n: int, profile: str) -> dict:
"""Set active profile for a board. Requires a session_id from logon.
async def profileset(self, board_n: int, profile: str) -> dict:
"""Set active profile for a board.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
board_n: The board to set the profile on.
profile: The profile name to use.
@@ -524,17 +569,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting the profile on board_n.
</details>
"""
return await self.send_command(
"profileset", parameters=f"{session_id},{board_n},{profile}"
)
return await self.send_privileged_command("profileset", board_n, profile)
async def reboot(self, session_id: str, board_n: int, delay_s: int = None) -> dict:
"""Reboot a board. Requires a session_id from logon.
async def reboot(self, board_n: int, delay_s: int = None) -> dict:
"""Reboot a board.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
board_n: The board to reboot.
delay_s: The number of seconds to delay until startup. If it is 0, the board will just stop. Optional.
@@ -542,24 +584,21 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of rebooting board_n.
</details>
"""
reboot_data = [session_id, str(board_n)]
reboot_data = [str(board_n)]
if delay_s is not None:
reboot_data.append(str(delay_s))
return await self.send_command("reboot", parameters=",".join(reboot_data))
return await self.send_privileged_command("reboot", *reboot_data)
async def rebootdevice(self, session_id: str) -> dict:
"""Reboot the miner. Requires a session_id from logon.
async def rebootdevice(self) -> dict:
"""Reboot the miner.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns:
A confirmation of rebooting the miner.
</details>
"""
return await self.send_command("rebootdevice", parameters=session_id)
return await self.send_privileged_command("rebootdevice")
async def removegroup(self, group_id: str) -> dict:
"""Remove a pool group.
@@ -575,19 +614,16 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("removegroup", parameters=group_id)
async def resetminer(self, session_id: str) -> dict:
"""Restart the mining process. Requires a session_id from logon.
async def resetminer(self) -> dict:
"""Restart the mining process.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns:
A confirmation of restarting the mining process.
</details>
"""
return await self.send_command("resetminer", parameters=session_id)
return await self.send_privileged_command("resetminer")
async def removepool(self, pool_id: int) -> dict:
"""Remove a pool.
@@ -614,7 +650,9 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_command("session")
async def tempctrlset(self, target: int, hot: int, dangerous: int) -> dict:
async def tempctrlset(
self, target: int = None, hot: int = None, dangerous: int = None
) -> dict:
"""Set temp control values.
<details>
<summary>Expand</summary>
@@ -629,7 +667,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
</details>
"""
return await self.send_command(
"tempctrlset", parameters=f"{target},{hot},{dangerous}"
"tempctrlset", target or "", hot or "", dangerous or ""
)
async def stats(self) -> dict:
@@ -668,7 +706,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of switching to the pool.
</details>
"""
return await self.send_command("switchpool", parameters=str(pool_id))
return await self.send_command("switchpool", pool_id)
async def tempctrl(self) -> dict:
"""Get temperature control data.
@@ -716,15 +754,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Board voltage values.
</details>
"""
return await self.send_command("frequencyget", parameters=str(board_n))
return await self.send_command("frequencyget", board_n)
async def voltageset(self, session_id: str, board_n: int, voltage: float) -> dict:
async def voltageset(self, board_n: int, voltage: float) -> dict:
"""Set voltage values.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
board_n: The board to set the voltage on.
voltage: The voltage to use.
@@ -732,20 +769,13 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting the voltage.
</details>
"""
return await self.send_command(
"voltageset", parameters=f"{session_id},{board_n},{voltage}"
)
return await self.send_privileged_command("voltageset", board_n, voltage)
async def wakeup(self, session_id: str) -> dict:
"""Take the miner out of sleep mode. Requires a session_id from logon.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
async def upgraderun(self):
"""
Send the 'updaterun' command to the miner.
Returns:
A confirmation of resuming mining.
</details>
The response from the miner after sending the 'updaterun' command.
"""
return await self.send_command("wakeup", parameters=session_id)
return await self.send_command("updaterun")

View File

@@ -17,10 +17,11 @@ from __future__ import annotations
import asyncio
import json
from pathlib import Path
from typing import Any
import aiofiles
import httpx
from pathlib import Path
from pyasic import settings
from pyasic.web.base import BaseWebAPI
@@ -414,10 +415,7 @@ class AntminerOldWebAPI(BaseWebAPI):
parameters = {
"file": (file.name, file_content, "application/octet-stream"),
"filename": file.name,
"keep_settings": keep_settings
"keep_settings": keep_settings,
}
return await self.send_command(
command="upgrade",
**parameters
)
return await self.send_command(command="upgrade", **parameters)

View File

@@ -0,0 +1,93 @@
from datetime import datetime, timedelta
from typing import Any, Dict
from betterproto import DATETIME_ZERO, TYPE_MAP, TYPE_MESSAGE, Casing, Message
# https://github.com/danielgtaylor/python-betterproto/pull/609
def to_pydict(
self, casing: Casing = Casing.CAMEL, include_default_values: bool = False
) -> Dict[str, Any]:
"""
Returns a python dict representation of this object.
Parameters
-----------
casing: :class:`Casing`
The casing to use for key values. Default is :attr:`Casing.CAMEL` for
compatibility purposes.
include_default_values: :class:`bool`
If ``True`` will include the default values of fields. Default is ``False``.
E.g. an ``int32`` field will be included with a value of ``0`` if this is
set to ``True``, otherwise this would be ignored.
Returns
--------
Dict[:class:`str`, Any]
The python dict representation of this object.
"""
output: Dict[str, Any] = {}
defaults = self._betterproto.default_gen
for field_name, meta in self._betterproto.meta_by_field_name.items():
field_is_repeated = defaults[field_name] is list
try:
value = getattr(self, field_name)
except AttributeError:
value = self._get_field_default(field_name)
cased_name = casing(field_name).rstrip("_") # type: ignore
if meta.proto_type == TYPE_MESSAGE:
if isinstance(value, datetime):
if (
value != DATETIME_ZERO
or include_default_values
or self._include_default_value_for_oneof(
field_name=field_name, meta=meta
)
):
output[cased_name] = value
elif isinstance(value, timedelta):
if (
value != timedelta(0)
or include_default_values
or self._include_default_value_for_oneof(
field_name=field_name, meta=meta
)
):
output[cased_name] = value
elif meta.wraps:
if value is not None or include_default_values:
output[cased_name] = value
elif field_is_repeated:
# Convert each item.
value = [i.to_pydict(casing, include_default_values) for i in value]
if value or include_default_values:
output[cased_name] = value
elif value is None:
if include_default_values:
output[cased_name] = None
elif (
value._serialized_on_wire
or include_default_values
or self._include_default_value_for_oneof(
field_name=field_name, meta=meta
)
):
output[cased_name] = value.to_pydict(casing, include_default_values)
elif meta.proto_type == TYPE_MAP:
for k in value:
if hasattr(value[k], "to_pydict"):
value[k] = value[k].to_pydict(casing, include_default_values)
if value or include_default_values:
output[cased_name] = value
elif (
value != self._get_field_default(field_name)
or include_default_values
or self._include_default_value_for_oneof(field_name=field_name, meta=meta)
):
output[cased_name] = value
return output
def patch():
Message.to_pydict = to_pydict

View File

@@ -26,6 +26,9 @@ from grpclib.client import Channel
from pyasic import settings
from pyasic.errors import APIError
from pyasic.web.base import BaseWebAPI
from pyasic.web.braiins_os.better_monkey import patch
patch()
from .proto.braiins.bos import *
from .proto.braiins.bos.v1 import *
@@ -206,7 +209,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_immersion_mode(
self,
enable: bool,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_immersion_mode",
@@ -227,7 +230,7 @@ class BOSerWebAPI(BaseWebAPI):
)
async def set_default_power_target(
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
self, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
) -> dict:
return await self.send_command(
"set_default_power_target",
@@ -238,7 +241,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_power_target(
self,
power_target: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_power_target",
@@ -251,7 +254,7 @@ class BOSerWebAPI(BaseWebAPI):
async def increment_power_target(
self,
power_target_increment: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"increment_power_target",
@@ -265,7 +268,7 @@ class BOSerWebAPI(BaseWebAPI):
async def decrement_power_target(
self,
power_target_decrement: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"decrement_power_target",
@@ -277,7 +280,7 @@ class BOSerWebAPI(BaseWebAPI):
)
async def set_default_hashrate_target(
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
self, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
) -> dict:
return await self.send_command(
"set_default_hashrate_target",
@@ -288,7 +291,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_hashrate_target(
self,
hashrate_target: float,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_hashrate_target",
@@ -302,7 +305,7 @@ class BOSerWebAPI(BaseWebAPI):
async def increment_hashrate_target(
self,
hashrate_target_increment: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"increment_hashrate_target",
@@ -318,7 +321,7 @@ class BOSerWebAPI(BaseWebAPI):
async def decrement_hashrate_target(
self,
hashrate_target_decrement: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"decrement_hashrate_target",
@@ -359,7 +362,7 @@ class BOSerWebAPI(BaseWebAPI):
self,
wattage_target: int = None,
hashrate_target: int = None,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
if wattage_target is not None and hashrate_target is not None:
logging.error(
@@ -459,7 +462,7 @@ class BOSerWebAPI(BaseWebAPI):
async def enable_hashboards(
self,
hashboard_ids: List[str],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"enable_hashboards",
@@ -472,7 +475,7 @@ class BOSerWebAPI(BaseWebAPI):
async def disable_hashboards(
self,
hashboard_ids: List[str],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"disable_hashboards",
@@ -485,7 +488,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_pool_groups(
self,
pool_groups: List[PoolGroupConfiguration],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_pool_groups",

View File

@@ -18,7 +18,7 @@ if TYPE_CHECKING:
@dataclass(eq=False, repr=False)
class ApiVersion(betterproto.Message):
"""LATEST_API_VERSION=1.2.0"""
"""LATEST_API_VERSION=1.3.0"""
major: int = betterproto.uint64_field(1)
minor: int = betterproto.uint64_field(2)

View File

@@ -20,125 +20,129 @@ if TYPE_CHECKING:
class SaveAction(betterproto.Enum):
"""Save action for different operations"""
SAVE_ACTION_UNSPECIFIED = 0
SAVE_ACTION_SAVE = 1
SAVE_ACTION_SAVE_AND_APPLY = 2
SAVE_ACTION_SAVE_AND_FORCE_APPLY = 3
UNSPECIFIED = 0
SAVE = 1
SAVE_AND_APPLY = 2
SAVE_AND_FORCE_APPLY = 3
class CoolingMode(betterproto.Enum):
COOLING_MODE_UNSPECIFIED = 0
COOLING_MODE_AUTO = 1
COOLING_MODE_MANUAL = 2
COOLING_MODE_DISABLED = 3
UNSPECIFIED = 0
AUTO = 1
MANUAL = 2
DISABLED = 3
class SensorLocation(betterproto.Enum):
SENSOR_LOCATION_UNSPECIFIED = 0
SENSOR_LOCATION_CHIP = 1
SENSOR_LOCATION_PCB = 2
UNSPECIFIED = 0
CHIP = 1
PCB = 2
class TunerMode(betterproto.Enum):
TUNER_MODE_UNSPECIFIED = 0
TUNER_MODE_POWER_TARGET = 1
TUNER_MODE_HASHRATE_TARGET = 2
UNSPECIFIED = 0
POWER_TARGET = 1
HASHRATE_TARGET = 2
class TunerState(betterproto.Enum):
TUNER_STATE_UNSPECIFIED = 0
TUNER_STATE_DISABLED = 1
TUNER_STATE_STABLE = 2
TUNER_STATE_TUNING = 3
TUNER_STATE_ERROR = 4
UNSPECIFIED = 0
DISABLED = 1
STABLE = 2
TUNING = 3
ERROR = 4
class LicenseType(betterproto.Enum):
LICENSE_TYPE_UNSPECIFIED = 0
LICENSE_TYPE_STANDARD = 1
LICENSE_TYPE_CUSTOM = 2
UNSPECIFIED = 0
STANDARD = 1
CUSTOM = 2
class Platform(betterproto.Enum):
"""Supported platforms"""
PLATFORM_UNSPECIFIED = 0
PLATFORM_AM1_S9 = 1
PLATFORM_AM2_S17 = 2
PLATFORM_AM3_BBB = 3
PLATFORM_AM3_AML = 4
PLATFORM_STM32MP157C_II1_AM2 = 5
PLATFORM_CVITEK_BM1_AM2 = 6
PLATFORM_ZYNQ_BM3_AM2 = 7
UNSPECIFIED = 0
AM1_S9 = 1
AM2_S17 = 2
AM3_BBB = 3
AM3_AML = 4
STM32MP157C_II1_AM2 = 5
CVITEK_BM1_AM2 = 6
ZYNQ_BM3_AM2 = 7
STM32MP157C_II2_BMM1 = 8
class BosMode(betterproto.Enum):
"""BOS modes enumeration"""
BOS_MODE_UNSPECIFIED = 0
BOS_MODE_UPGRADE = 1
BOS_MODE_RECOVERY = 2
BOS_MODE_SD = 3
BOS_MODE_NAND = 4
BOS_MODE_EMMC = 5
UNSPECIFIED = 0
UPGRADE = 1
RECOVERY = 2
SD = 3
NAND = 4
EMMC = 5
class MinerBrand(betterproto.Enum):
MINER_BRAND_UNSPECIFIED = 0
MINER_BRAND_ANTMINER = 1
MINER_BRAND_WHATSMINER = 2
UNSPECIFIED = 0
ANTMINER = 1
WHATSMINER = 2
class MinerModel(betterproto.Enum):
"""Deprecated: This enumeration is not longer maintained"""
MINER_MODEL_UNSPECIFIED = 0
MINER_MODEL_ANTMINER_S9 = 1
MINER_MODEL_ANTMINER_X17 = 2
MINER_MODEL_ANTMINER_S17 = 3
MINER_MODEL_ANTMINER_S17_PLUS = 4
MINER_MODEL_ANTMINER_S17_PRO = 5
MINER_MODEL_ANTMINER_S17E = 6
MINER_MODEL_ANTMINER_T17 = 7
MINER_MODEL_ANTMINER_T17E = 8
MINER_MODEL_ANTMINER_T17_PLUS = 9
MINER_MODEL_ANTMINER_X19 = 10
MINER_MODEL_ANTMINER_S19 = 11
MINER_MODEL_ANTMINER_S19_PRO = 12
MINER_MODEL_ANTMINER_S19_PLUS = 13
MINER_MODEL_ANTMINER_S19J = 14
MINER_MODEL_ANTMINER_S19J_PRO = 15
MINER_MODEL_ANTMINER_S19A = 16
MINER_MODEL_ANTMINER_S19A_PRO = 17
MINER_MODEL_ANTMINER_S19XP = 18
MINER_MODEL_ANTMINER_T19 = 19
MINER_MODEL_ANTMINER_S19J_PRO_PLUS = 20
UNSPECIFIED = 0
ANTMINER_S9 = 1
ANTMINER_X17 = 2
ANTMINER_S17 = 3
ANTMINER_S17_PLUS = 4
ANTMINER_S17_PRO = 5
ANTMINER_S17E = 6
ANTMINER_T17 = 7
ANTMINER_T17E = 8
ANTMINER_T17_PLUS = 9
ANTMINER_X19 = 10
ANTMINER_S19 = 11
ANTMINER_S19_PRO = 12
ANTMINER_S19_PLUS = 13
ANTMINER_S19J = 14
ANTMINER_S19J_PRO = 15
ANTMINER_S19A = 16
ANTMINER_S19A_PRO = 17
ANTMINER_S19XP = 18
ANTMINER_T19 = 19
ANTMINER_S19J_PRO_PLUS = 20
class MinerStatus(betterproto.Enum):
MINER_STATUS_UNSPECIFIED = 0
MINER_STATUS_NOT_STARTED = 1
MINER_STATUS_NORMAL = 2
MINER_STATUS_PAUSED = 3
MINER_STATUS_SUSPENDED = 4
MINER_STATUS_RESTRICTED = 5
UNSPECIFIED = 0
NOT_STARTED = 1
NORMAL = 2
PAUSED = 3
SUSPENDED = 4
RESTRICTED = 5
class SupportArchiveFormat(betterproto.Enum):
"""Enumeration for support archive format"""
SUPPORT_ARCHIVE_FORMAT_UNSPECIFIED = 0
SUPPORT_ARCHIVE_FORMAT_ZIP = 1
UNSPECIFIED = 0
ZIP = 1
"""Compressed zip format"""
SUPPORT_ARCHIVE_FORMAT_BOS = 2
BOS = 2
"""BOS custom format"""
ZIP_ENCRYPTED = 3
"""Compressed encrypted zip format"""
class NetworkProtocol(betterproto.Enum):
NETWORK_PROTOCOL_UNSPECIFIED = 0
NETWORK_PROTOCOL_DHCP = 1
NETWORK_PROTOCOL_STATIC = 2
UNSPECIFIED = 0
DHCP = 1
STATIC = 2
@dataclass(eq=False, repr=False)
@@ -268,8 +272,8 @@ class LoginResponse(betterproto.Message):
timeout_s: int = betterproto.uint32_field(2)
"""
Authentication token validity/timeout in seconds. Token validity refreshed
to this value with each request.
Authentication token validity/timeout in seconds.
Token validity refreshed to this value with each request.
"""
@@ -277,9 +281,7 @@ class LoginResponse(betterproto.Message):
class SetPasswordRequest(betterproto.Message):
"""Request for set password action."""
password: Optional[str] = betterproto.string_field(
1, optional=True, group="_password"
)
password: Optional[str] = betterproto.string_field(1, optional=True)
@dataclass(eq=False, repr=False)
@@ -335,8 +337,8 @@ class BasesPoints(betterproto.Message):
bsp: int = betterproto.uint32_field(1)
"""
A basis point is one hundredth of 1 percentage point. For example: 1bps =
0.01%, 250bps = 2.5%
A basis point is one hundredth of 1 percentage point.
For example: 1bps = 0.01%, 250bps = 2.5%
"""
@@ -409,9 +411,9 @@ class VoltageConstraints(betterproto.Message):
@dataclass(eq=False, repr=False)
class CoolingAutoMode(betterproto.Message):
"""
The temperature control modes. Miner software tries to regulate the fan
speed so that miner temperature is approximately at the target temperature.
The allowed temperature range is 0-200 degree Celsius.
The temperature control modes.
Miner software tries to regulate the fan speed so that miner temperature is approximately at the target temperature.
The allowed temperature range is 0-200 degree Celsius.
"""
target_temperature: "Temperature" = betterproto.message_field(1)
@@ -422,8 +424,7 @@ class CoolingAutoMode(betterproto.Message):
dangerous_temperature: "Temperature" = betterproto.message_field(3)
"""
Temperature threshold at which BOSMiner shuts down in order to prevent
overheating and damaging the miner.
Temperature threshold at which BOSMiner shuts down in order to prevent overheating and damaging the miner.
"""
@@ -433,12 +434,11 @@ class CoolingManualMode(betterproto.Message):
Fans are kept at a fixed, user-defined speed, no matter the temperature.
"""
fan_speed_ratio: Optional[float] = betterproto.double_field(
1, optional=True, group="_fan_speed_ratio"
)
fan_speed_ratio: Optional[float] = betterproto.double_field(1, optional=True)
"""
User defined fan speed expressed as a ratio between 0.0 and 1.0 where 0.0
means completely turned off and 1.0 means running at full speed possible
User defined fan speed expressed as a ratio between 0.0 and 1.0
where 0.0 means completely turned off and
1.0 means running at full speed possible
"""
hot_temperature: "Temperature" = betterproto.message_field(2)
@@ -446,8 +446,7 @@ class CoolingManualMode(betterproto.Message):
dangerous_temperature: "Temperature" = betterproto.message_field(3)
"""
Temperature threshold at which BOSMiner shuts down in order to prevent
overheating and damaging the miner.
Temperature threshold at which BOSMiner shuts down in order to prevent overheating and damaging the miner.
"""
@@ -455,20 +454,17 @@ class CoolingManualMode(betterproto.Message):
class CoolingDisabledMode(betterproto.Message):
"""Disable temperature control. May be dangerous."""
fan_speed_ratio: Optional[float] = betterproto.double_field(
1, optional=True, group="_fan_speed_ratio"
)
fan_speed_ratio: Optional[float] = betterproto.double_field(1, optional=True)
"""
User defined fan speed expressed as a ratio between 0.0 and 1.0 where 0.0
means completely turned off and 1.0 means running at full speed possible
User defined fan speed expressed as a ratio between 0.0 and 1.0
where 0.0 means completely turned off and
1.0 means running at full speed possible
"""
@dataclass(eq=False, repr=False)
class CoolingConfiguration(betterproto.Message):
minimum_required_fans: Optional[int] = betterproto.uint32_field(
1, optional=True, group="_minimum_required_fans"
)
minimum_required_fans: Optional[int] = betterproto.uint32_field(1, optional=True)
auto: "CoolingAutoMode" = betterproto.message_field(2, group="mode")
manual: "CoolingManualMode" = betterproto.message_field(3, group="mode")
disabled: "CoolingDisabledMode" = betterproto.message_field(4, group="mode")
@@ -488,23 +484,19 @@ class CoolingConstraints(betterproto.Message):
class FanState(betterproto.Message):
"""Structure which contain info about one specific miner fan."""
position: Optional[int] = betterproto.uint32_field(
1, optional=True, group="_position"
)
position: Optional[int] = betterproto.uint32_field(1, optional=True)
"""Fan positions/ID"""
rpm: int = betterproto.uint32_field(2)
"""Actual fan RPM (Revolutions/Rotation Per Minute)"""
target_speed_ratio: Optional[float] = betterproto.double_field(
3, optional=True, group="_target_speed_ratio"
)
target_speed_ratio: Optional[float] = betterproto.double_field(3, optional=True)
"""Actual fan speed ratio(PWM) in range 0.0 - 1.0"""
@dataclass(eq=False, repr=False)
class TemperatureSensor(betterproto.Message):
id: Optional[int] = betterproto.uint32_field(1, optional=True, group="_id")
id: Optional[int] = betterproto.uint32_field(1, optional=True)
"""Sensor id"""
location: "SensorLocation" = betterproto.enum_field(2)
@@ -523,7 +515,10 @@ class GetCoolingStateRequest(betterproto.Message):
@dataclass(eq=False, repr=False)
class GetCoolingStateResponse(betterproto.Message):
"""Response to get current fan states and temperature measurements"""
"""
Response to get current fan states and
temperature measurements
"""
fans: List["FanState"] = betterproto.message_field(1)
"""All Fans state"""
@@ -551,12 +546,10 @@ class SetImmersionModeResponse(betterproto.Message):
@dataclass(eq=False, repr=False)
class TunerConfiguration(betterproto.Message):
enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(1, optional=True)
"""Flag if tuner is enabled"""
tuner_mode: Optional["TunerMode"] = betterproto.enum_field(
2, optional=True, group="_tuner_mode"
)
tuner_mode: Optional["TunerMode"] = betterproto.enum_field(2, optional=True)
"""Tuner mode"""
power_target: "Power" = betterproto.message_field(3)
@@ -583,7 +576,7 @@ class TunerConstraints(betterproto.Message):
@dataclass(eq=False, repr=False)
class DpsConfiguration(betterproto.Message):
enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(1, optional=True)
"""Flag if Dynamic Performance Scaling is enabled"""
power_step: "Power" = betterproto.message_field(2)
@@ -598,9 +591,7 @@ class DpsConfiguration(betterproto.Message):
min_hashrate_target: "TeraHashrate" = betterproto.message_field(5)
"""Dynamic Performance Scaling minimal hashrate target"""
shutdown_enabled: Optional[bool] = betterproto.bool_field(
6, optional=True, group="_shutdown_enabled"
)
shutdown_enabled: Optional[bool] = betterproto.bool_field(6, optional=True)
"""Flag if shutdown for Dynamic Performance Scaling is enabled"""
shutdown_duration: "Hours" = betterproto.message_field(7)
@@ -879,17 +870,13 @@ class SetDpsRequest(betterproto.Message):
save_action: "SaveAction" = betterproto.enum_field(1)
"""Save action"""
enable: Optional[bool] = betterproto.bool_field(2, optional=True, group="_enable")
enable: Optional[bool] = betterproto.bool_field(2, optional=True)
"""Flag if Dynamic Performance Scaling should be enabled"""
enable_shutdown: Optional[bool] = betterproto.bool_field(
3, optional=True, group="_enable_shutdown"
)
enable_shutdown: Optional[bool] = betterproto.bool_field(3, optional=True)
"""Flag if shutdown for Dynamic Performance Scaling should be enabled"""
shutdown_duration: Optional["Hours"] = betterproto.message_field(
4, optional=True, group="_shutdown_duration"
)
shutdown_duration: Optional["Hours"] = betterproto.message_field(4, optional=True)
"""Dynamic Performance Scaling shutdown duration"""
target: "DpsTarget" = betterproto.message_field(5)
@@ -898,17 +885,13 @@ class SetDpsRequest(betterproto.Message):
@dataclass(eq=False, repr=False)
class SetDpsResponse(betterproto.Message):
enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(1, optional=True)
"""Flag if Dynamic Performance Scaling is enabled"""
shutdown_enabled: Optional[bool] = betterproto.bool_field(
2, optional=True, group="_shutdown_enabled"
)
shutdown_enabled: Optional[bool] = betterproto.bool_field(2, optional=True)
"""Flag if shutdown for Dynamic Performance Scaling should be enabled"""
shutdown_duration: Optional["Hours"] = betterproto.message_field(
3, optional=True, group="_shutdown_duration"
)
shutdown_duration: Optional["Hours"] = betterproto.message_field(3, optional=True)
"""Dynamic Performance Scaling shutdown duration"""
power_target: "DpsPowerTarget" = betterproto.message_field(4)
@@ -935,7 +918,7 @@ class HashboardConfig(betterproto.Message):
id: str = betterproto.string_field(1)
"""Hashboard id"""
enabled: Optional[bool] = betterproto.bool_field(2, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(2, optional=True)
"""Flag if HB si enabled"""
frequency: "Frequency" = betterproto.message_field(3)
@@ -1019,9 +1002,9 @@ class Quota(betterproto.Message):
@dataclass(eq=False, repr=False)
class FixedShareRatio(betterproto.Message):
"""
Structure for fixed share ratio load balance strategy Fixed share ratio is
value between 0.0 to 1.0 where 1.0 represents that all work is generated
from the group
Structure for fixed share ratio load balance strategy
Fixed share ratio is value between 0.0 to 1.0 where 1.0 represents that all work is
generated from the group
"""
value: float = betterproto.double_field(1)
@@ -1058,12 +1041,10 @@ class PoolConfiguration(betterproto.Message):
user: str = betterproto.string_field(3)
"""Pool connection user"""
password: Optional[str] = betterproto.string_field(
4, optional=True, group="_password"
)
password: Optional[str] = betterproto.string_field(4, optional=True)
"""Pool connection password if set"""
enabled: Optional[bool] = betterproto.bool_field(5, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(5, optional=True)
"""Flag if pool connection is enabled"""
@@ -1130,9 +1111,7 @@ class PoolStats(betterproto.Message):
generated_work: int = betterproto.uint64_field(6)
"""Generated work"""
last_share_time: Optional[datetime] = betterproto.message_field(
7, optional=True, group="_last_share_time"
)
last_share_time: Optional[datetime] = betterproto.message_field(7, optional=True)
"""Last share time"""
@@ -1154,9 +1133,9 @@ class GetPoolGroupsResponse(betterproto.Message):
@dataclass(eq=False, repr=False)
class CreatePoolGroupRequest(betterproto.Message):
"""
Request for pool group create action group.uid must not be specified (it
will be generated) group.pools[].uid must not be specified (it will be
generated)
Request for pool group create action
group.uid must not be specified (it will be generated)
group.pools[].uid must not be specified (it will be generated)
"""
save_action: "SaveAction" = betterproto.enum_field(1)
@@ -1177,9 +1156,9 @@ class CreatePoolGroupResponse(betterproto.Message):
@dataclass(eq=False, repr=False)
class UpdatePoolGroupRequest(betterproto.Message):
"""
Request for pool group update action group.uid must be specified and
represents unique id of group which will be updated group.pools[].uid must
not be specified (it will be generated)
Request for pool group update action
group.uid must be specified and represents unique id of group which will be updated
group.pools[].uid must not be specified (it will be generated)
"""
save_action: "SaveAction" = betterproto.enum_field(1)
@@ -1221,16 +1200,15 @@ class SetPoolGroupsRequest(betterproto.Message):
save_action: "SaveAction" = betterproto.enum_field(1)
"""
Save action SAVE just update config but changes will not be applied
SAVE_AND_APPLY and SAVE_AND_FORCE_APPLY are equal for this method. Pools
config will be updated and changes will be applied that will trigger
restart.
Save action
SAVE just update config but changes will not be applied
SAVE_AND_APPLY and SAVE_AND_FORCE_APPLY are equal for this method. Pools config will be updated and changes will be applied that will trigger restart.
"""
pool_groups: List["PoolGroupConfiguration"] = betterproto.message_field(2)
"""
Pool groups configuration `uid` must not be specified (it will be
generated)
Pool groups configuration
`uid` must not be specified (it will be generated)
"""
@@ -1292,9 +1270,8 @@ class NoneLicense(betterproto.Message):
time_to_restricted: int = betterproto.uint32_field(1)
"""
BOS Initialization timeout - number of seconds elapsed since bosminer start
i.e., number of seconds BOS will start mining in restricted mode burning 5%
of hashrate For more, see Section 3.10 of
https://braiins.com/os/plus/license
i.e., number of seconds BOS will start mining in restricted mode burning 5% of hashrate
For more, see Section 3.10 of https://braiins.com/os/plus/license
"""
@@ -1313,9 +1290,8 @@ class ValidLicense(betterproto.Message):
time_to_restricted: int = betterproto.uint32_field(3)
"""
Current license expiration - number of seconds since the moment the license
was received i.e., number of seconds BOS will start mining in restricted
mode burning 15% of hashrate
Current license expiration - number of seconds since the moment the license was received
i.e., number of seconds BOS will start mining in restricted mode burning 15% of hashrate
"""
dev_fee: "BasesPoints" = betterproto.message_field(4)
@@ -1388,7 +1364,8 @@ class MinerIdentity(betterproto.Message):
brand: "MinerBrand" = betterproto.enum_field(1)
model: "MinerModel" = betterproto.enum_field(2)
"""
Deprecated: Use miner_model instead. This field is no longer supported.
Deprecated: Use miner_model instead.
This field is no longer supported.
"""
name: str = betterproto.string_field(3)
@@ -1564,7 +1541,7 @@ class Hashboard(betterproto.Message):
stats: "WorkSolverStats" = betterproto.message_field(8)
"""Hashboard stats"""
model: Optional[str] = betterproto.string_field(9, optional=True, group="_model")
model: Optional[str] = betterproto.string_field(9, optional=True)
"""Hashboard model"""
@@ -1644,9 +1621,7 @@ class GetNetworkConfigurationResponse(betterproto.Message):
class SetNetworkConfigurationRequest(betterproto.Message):
dhcp: "Dhcp" = betterproto.message_field(1, group="protocol")
static: "Static" = betterproto.message_field(2, group="protocol")
hostname: Optional[str] = betterproto.string_field(
3, optional=True, group="_hostname"
)
hostname: Optional[str] = betterproto.string_field(3, optional=True)
"""Hostname. Existing value will be preserved if this field is not set."""
@@ -1691,27 +1666,21 @@ class GetNetworkInfoRequest(betterproto.Message):
@dataclass(eq=False, repr=False)
class GetNetworkInfoResponse(betterproto.Message):
"""
Response message for GetCurrentNetworkConfiguration Represents the current
network configuration for the default network interface. Only IPv4 is
supported.
Response message for GetCurrentNetworkConfiguration
Represents the current network configuration for the default network interface.
Only IPv4 is supported.
"""
name: str = betterproto.string_field(1)
"""Name of the network interface"""
mac_address: Optional[str] = betterproto.string_field(
2, optional=True, group="_mac_address"
)
mac_address: Optional[str] = betterproto.string_field(2, optional=True)
"""MAC address of the network interface"""
hostname: Optional[str] = betterproto.string_field(
3, optional=True, group="_hostname"
)
hostname: Optional[str] = betterproto.string_field(3, optional=True)
"""Miner hostname"""
protocol: Optional["NetworkProtocol"] = betterproto.enum_field(
4, optional=True, group="_protocol"
)
protocol: Optional["NetworkProtocol"] = betterproto.enum_field(4, optional=True)
"""Network protocol"""
dns_servers: List[str] = betterproto.string_field(5)
@@ -1720,9 +1689,7 @@ class GetNetworkInfoResponse(betterproto.Message):
networks: List["IpNetwork"] = betterproto.message_field(6)
"""List of assigned IP addresses"""
default_gateway: Optional[str] = betterproto.string_field(
7, optional=True, group="_default_gateway"
)
default_gateway: Optional[str] = betterproto.string_field(7, optional=True)
"""Default gateway/route for the interface"""
@@ -2332,7 +2299,7 @@ class MinerServiceStub(betterproto.ServiceStub):
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> AsyncIterator["GetMinerStatusResponse"]:
) -> AsyncIterator[GetMinerStatusResponse]:
async for response in self._unary_stream(
"/braiins.bos.v1.MinerService/GetMinerStatus",
get_miner_status_request,
@@ -2418,7 +2385,7 @@ class MinerServiceStub(betterproto.ServiceStub):
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> AsyncIterator["GetSupportArchiveResponse"]:
) -> AsyncIterator[GetSupportArchiveResponse]:
async for response in self._unary_stream(
"/braiins.bos.v1.MinerService/GetSupportArchive",
get_support_archive_request,
@@ -3195,7 +3162,7 @@ class MinerServiceBase(ServiceBase):
async def get_miner_status(
self, get_miner_status_request: "GetMinerStatusRequest"
) -> AsyncIterator["GetMinerStatusResponse"]:
) -> AsyncIterator[GetMinerStatusResponse]:
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
yield GetMinerStatusResponse()
@@ -3221,7 +3188,7 @@ class MinerServiceBase(ServiceBase):
async def get_support_archive(
self, get_support_archive_request: "GetSupportArchiveRequest"
) -> AsyncIterator["GetSupportArchiveResponse"]:
) -> AsyncIterator[GetSupportArchiveResponse]:
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
yield GetSupportArchiveResponse()

View File

@@ -127,6 +127,9 @@ class InnosiliconWebAPI(BaseWebAPI):
async def get_all(self) -> dict:
return await self.send_command("getAll")
async def summary(self) -> dict:
return await self.send_command("summary")
async def get_error_detail(self) -> dict:
return await self.send_command("getErrorDetail")

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