Compare commits

...

65 Commits

Author SHA1 Message Date
UpstreamData
2eb6697e9a version: bump version number 2024-02-21 13:23:38 -07:00
UpstreamData
c5817fcc36 docs: Update docs. 2024-02-21 13:22:40 -07:00
Brett Rowan
f2391bcb2d Merge pull request #110 from jpcomps/add_blockminer_support
ePIC: Add Blockminer support
2024-02-21 13:21:45 -07:00
Brett Rowan
bc3bd9e5da Fix issue with possible type hint incompatability. 2024-02-21 13:20:41 -07:00
UpstreamData
3f0959d75e docs: Update docs. 2024-02-21 12:43:46 -07:00
UpstreamData
31f7b56724 docs: Update docs. 2024-02-21 12:39:40 -07:00
UpstreamData
072954d755 docs: Update docs. 2024-02-21 12:39:08 -07:00
UpstreamData
d271e0f9c8 version: bump version number. 2024-02-21 12:33:21 -07:00
UpstreamData
8f1408ce17 bug: fix sending privileged commands on BTMiner using timeout as the port, and capture positional arguments in _send_bytes to prevent this. 2024-02-21 12:32:56 -07:00
John-Paul Compagnone
825d1f4cfb fix checks 2024-02-20 20:39:22 -05:00
John-Paul Compagnone
c6bcd7e05a fix capitalization 2024-02-20 20:33:30 -05:00
John-Paul Compagnone
5d80051f3b more missing i 2024-02-20 18:15:40 -05:00
John-Paul Compagnone
b71c448199 add missing i 2024-02-20 18:12:19 -05:00
JP Compagnone
c82148412c Merge branch 'UpstreamData:master' into add_blockminer_support 2024-02-20 18:01:16 -05:00
UpstreamData
a582ee63a0 bug: use force apply to fix DPS bug with setting wattage. 2024-02-20 10:11:57 -07:00
UpstreamData
db7c19c486 feature: add some miner support. 2024-02-20 09:47:28 -07:00
JP Compagnone
599f71da19 Merge branch 'UpstreamData:master' into add_blockminer_support 2024-02-19 19:04:32 -05:00
UpstreamData
0995744d90 version: bump version number. 2024-02-14 13:48:07 -07:00
UpstreamData
4073a27aba bug: update miner handling with unknown models, but known makes. 2024-02-14 13:47:34 -07:00
UpstreamData
bec9c31c97 docs: Update supported miners location. 2024-02-12 10:38:22 -07:00
UpstreamData
acdd615c53 docs: update docs location. 2024-02-12 10:37:33 -07:00
UpstreamData
8091617ee2 feature: add supports_power_modes flag. 2024-02-12 09:26:09 -07:00
John-Paul Compagnone
c25ff6fcef add ePIC Blockminer 520i support 2024-02-11 10:55:49 -05:00
John-Paul Compagnone
ab0dcd607b make ePIC api calls more reliable 2024-02-10 21:59:08 -05:00
Brett Rowan
ce288e472f version: bump version number. 2024-02-10 17:51:12 -07:00
Brett Rowan
02d8f25daf Merge pull request #108 from jpcomps/master
add ePIC UMC S21 support, fix HB generation by using capabilities
2024-02-10 17:50:37 -07:00
John-Paul Compagnone
a76d1c6149 add ePIC UMC S21 support, fix HB generation by using capabilities 2024-02-10 19:36:07 -05:00
Brett Rowan
17f5eade19 version: bump version number. 2024-02-10 16:45:49 -07:00
Brett Rowan
b6a2a5054b bug: fix goldshell sleep mode again. 2024-02-10 16:45:29 -07:00
Brett Rowan
5984338c64 version: bump version number. 2024-02-10 15:47:25 -07:00
Brett Rowan
07d1c48e33 bug: fix goldshell config/power modes. 2024-02-10 15:46:55 -07:00
Brett Rowan
d2abae947c version: bump version number. 2024-02-10 15:39:34 -07:00
Brett Rowan
e4a0f2451a Attempt to fix goldshell mode issues. 2024-02-10 15:39:00 -07:00
b-rowan
880c598b1a version: bump version number. 2024-02-10 14:26:09 -07:00
b-rowan
3632c2c4d8 feature: add support for goldshell shutdown. 2024-02-10 14:25:42 -07:00
b-rowan
09bc9686ae feature: add support for goldshell mode settings. 2024-02-10 14:23:17 -07:00
b-rowan
34584ab098 feature: add support for KDBoxPro and KDBoxII. 2024-02-10 13:56:00 -07:00
UpstreamData
554d99ca08 bug: update API unlocker format. 2024-02-09 14:25:14 -07:00
UpstreamData
5c5d688ffa Update api opener. 2024-02-09 13:39:48 -07:00
b-rowan
c50d55e87c version: bump version number. 2024-02-07 20:16:26 -07:00
b-rowan
5e5516bfb3 bug: fix serial numbers for antminer. 2024-02-07 20:15:38 -07:00
UpstreamData
4b068c57c5 version: bump version number. 2024-02-07 11:17:29 -07:00
UpstreamData
203f199aec feature: add wmt.pyasic.org. 2024-02-07 11:09:27 -07:00
b-rowan
895f17aaf9 version: bump version number. 2024-02-03 00:36:44 -07:00
b-rowan
8a64ff3559 bug: swap to asyncio.read() in base RPC to try to handle possible missed messages. 2024-02-03 00:36:03 -07:00
UpstreamData
4c45d356c4 version: bump version number. 2024-02-02 10:07:08 -07:00
UpstreamData
4dec329f11 bug: Try to return something when checking vnish fw version. 2024-02-02 10:06:33 -07:00
UpstreamData
b563ed118e bug: fix vnish firmware version bug. 2024-02-02 10:05:34 -07:00
UpstreamData
75b2ec40b1 bug: fix ePIC config parsing to use hashrate tuning instead of power tuning. 2024-01-31 09:21:32 -07:00
b-rowan
d9adaf6667 version: bump version number. 2024-01-30 21:41:49 -07:00
b-rowan
9343308f41 feature: Add support for new whatsminers, and try to handle whatsminer errors when receiving data. 2024-01-30 21:41:10 -07:00
UpstreamData
88769e40ae version: bump version number. 2024-01-30 13:34:24 -07:00
UpstreamData
be45eb7400 bug: fix issues with bosminer multicommand, and update X17 to use BOSMiner instead of BOSer. 2024-01-30 13:34:00 -07:00
b-rowan
2f719a03a4 version: bump version number. 2024-01-29 20:57:01 -07:00
b-rowan
64196f9754 bug: update whatsminer set_target_freq to match docs. 2024-01-29 20:56:36 -07:00
UpstreamData
49a77f1b79 version: bump version number. 2024-01-29 12:47:54 -07:00
UpstreamData
3838c4f2f9 bug: fix missing validation import for BTMiner. 2024-01-29 12:47:30 -07:00
UpstreamData
80d89c95b5 version: bump version number. 2024-01-29 12:33:07 -07:00
UpstreamData
30cd8b5cfe bug: fix some issues with rpc renaming. 2024-01-29 12:32:54 -07:00
b-rowan
c443170f78 refactor: improve epic web send_command implementation. 2024-01-27 09:42:35 -07:00
b-rowan
a2c2aa2377 version: bump version number. 2024-01-27 09:26:13 -07:00
b-rowan
4f0eb49a02 bug: fix some issues with epic send config. 2024-01-27 09:25:20 -07:00
b-rowan
a821357b4f Merge pull request #102 from jpcomps/master
fix send_config for ePIC
2024-01-27 09:05:23 -07:00
John-Paul Compagnone
3c7679a22d fix send_config for ePIC 2024-01-27 10:50:37 -05:00
UpstreamData
a52737e236 refactor: add some type hints for epic config. 2024-01-26 13:58:08 -07:00
80 changed files with 1134 additions and 206 deletions

View File

@@ -9,7 +9,7 @@
[![Commit Activity - master](https://img.shields.io/github/commit-activity/y/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/commits/master/) [![Commit Activity - master](https://img.shields.io/github/commit-activity/y/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/commits/master/)
[![Code Style - Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Code Style - Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Read The Docs - Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/) [![Read The Docs - Docs](https://img.shields.io/readthedocs/pyasic)](https://docs.pyasic.org)
[![License - Apache 2.0](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt) [![License - Apache 2.0](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
--- ---
@@ -17,7 +17,7 @@
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast. Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
[Click here to view supported miner types](miners/supported_types.md) [Click here to view supported miner types](https://docs.pyasic.org/en/latest/miners/supported_types/)
--- ---
## Getting started ## Getting started

View File

@@ -27,6 +27,8 @@ def backend_str(backend: MinerTypes) -> str:
match backend: match backend:
case MinerTypes.ANTMINER: case MinerTypes.ANTMINER:
return "Stock Firmware Antminers" return "Stock Firmware Antminers"
case MinerTypes.AURADINE:
return "Stock Firmware Auradine Miners"
case MinerTypes.AVALONMINER: case MinerTypes.AVALONMINER:
return "Stock Firmware Avalonminers" return "Stock Firmware Avalonminers"
case MinerTypes.VNISH: case MinerTypes.VNISH:
@@ -45,6 +47,8 @@ def backend_str(backend: MinerTypes) -> str:
return "Stock Firmware Goldshells" return "Stock Firmware Goldshells"
case MinerTypes.LUX_OS: case MinerTypes.LUX_OS:
return "LuxOS Firmware Miners" return "LuxOS Firmware Miners"
case MinerTypes.EPIC:
return "ePIC Firmware Miners"
def create_url_str(mtype: str): def create_url_str(mtype: str):

View File

@@ -50,49 +50,49 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17 (BOS) ## S17 (BOS+)
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17 ::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17+ (BOS) ## S17+ (BOS+)
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus ::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17 Pro (BOS) ## S17 Pro (BOS+)
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Pro ::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Pro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S17e (BOS) ## S17e (BOS+)
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17e ::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17e
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T17 (BOS) ## T17 (BOS+)
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17 ::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T17+ (BOS) ## T17+ (BOS+)
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17Plus ::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17Plus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T17e (BOS) ## T17e (BOS+)
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17e ::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17e
handler: python handler: python
options: options:

View File

@@ -85,6 +85,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro Hydro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProHydro
handler: python
options:
show_root_heading: false
heading_level: 4
## T19 ## T19
::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19 ::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19
handler: python handler: python
@@ -92,49 +99,91 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 (BOS) ## S19
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19 ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 Pro (BOS) ## S19+
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Pro ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Pro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j (BOS) ## S19a
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 4
## S19a Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19j ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19j
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j No PIC (BOS) ## S19j No PIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro (BOS) ## S19j Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro (BOS) ## S19j Pro
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T19 (BOS) ## S19j Pro+
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19k Pro No PIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 XP
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 4
## T19
::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19 ::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19
handler: python handler: python
options: options:
@@ -225,6 +274,20 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro+ (ePIC)
::: pyasic.miners.antminer.epic.X19.S19.ePICS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19k Pro (ePIC)
::: pyasic.miners.antminer.epic.X19.S19.ePICS19kPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 XP (ePIC) ## S19 XP (ePIC)
::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP ::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP
handler: python handler: python
@@ -232,3 +295,10 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 (LuxOS)
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -0,0 +1,10 @@
# pyasic
## X21 Models
## S21 (ePIC)
::: pyasic.miners.antminer.epic.X21.S21.ePICS21
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -29,3 +29,10 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## L3+ (VNish)
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -8,3 +8,10 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## L7 (VNish)
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -36,14 +36,14 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S9 (BOS) ## S9 (BOS+)
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9 ::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## T9 (Hiveon) ## T9 (Hive)
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9 ::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
handler: python handler: python
options: options:

View File

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

View File

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

View File

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

View File

@@ -21,3 +21,4 @@
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4

View File

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

View File

@@ -2,8 +2,9 @@
## XMax Models ## XMax Models
## KD Max ## KD Max
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.KDMax ::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4

View File

@@ -7,3 +7,4 @@
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4

View File

@@ -7,3 +7,4 @@
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4

View File

@@ -78,6 +78,7 @@ details {
<li><a href="../antminer/X19#s19-xp">S19 XP</a></li> <li><a href="../antminer/X19#s19-xp">S19 XP</a></li>
<li><a href="../antminer/X19#s19a">S19a</a></li> <li><a href="../antminer/X19#s19a">S19a</a></li>
<li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li> <li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li>
<li><a href="../antminer/X19#s19-pro-hydro">S19 Pro Hydro</a></li>
<li><a href="../antminer/X19#t19">T19</a></li> <li><a href="../antminer/X19#t19">T19</a></li>
</ul> </ul>
</details> </details>
@@ -286,13 +287,39 @@ details {
<li><a href="../whatsminer/M5X#m50s_1_1-vk30">M50S++ VK30</a></li> <li><a href="../whatsminer/M5X#m50s_1_1-vk30">M50S++ VK30</a></li>
<li><a href="../whatsminer/M5X#m53-vh30">M53 VH30</a></li> <li><a href="../whatsminer/M5X#m53-vh30">M53 VH30</a></li>
<li><a href="../whatsminer/M5X#m53s-vh30">M53S VH30</a></li> <li><a href="../whatsminer/M5X#m53s-vh30">M53S VH30</a></li>
<li><a href="../whatsminer/M5X#m53s-vj40">M53S VJ40</a></li>
<li><a href="../whatsminer/M5X#m53s_1-vj30">M53S+ VJ30</a></li> <li><a href="../whatsminer/M5X#m53s_1-vj30">M53S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m53s_1_1-vk10">M53S++ VK10</a></li>
<li><a href="../whatsminer/M5X#m56-vh30">M56 VH30</a></li> <li><a href="../whatsminer/M5X#m56-vh30">M56 VH30</a></li>
<li><a href="../whatsminer/M5X#m56s-vh30">M56S VH30</a></li> <li><a href="../whatsminer/M5X#m56s-vh30">M56S VH30</a></li>
<li><a href="../whatsminer/M5X#m56s_1-vj30">M56S+ VJ30</a></li> <li><a href="../whatsminer/M5X#m56s_1-vj30">M56S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m59-vh30">M59 VH30</a></li> <li><a href="../whatsminer/M5X#m59-vh30">M59 VH30</a></li>
</ul> </ul>
</details> </details>
<details>
<summary>M6X Series:</summary>
<ul>
<li><a href="../whatsminer/M6X#m60-vk10">M60 VK10</a></li>
<li><a href="../whatsminer/M6X#m60-vk20">M60 VK20</a></li>
<li><a href="../whatsminer/M6X#m60-vk30">M60 VK30</a></li>
<li><a href="../whatsminer/M6X#m60-vk40">M60 VK40</a></li>
<li><a href="../whatsminer/M6X#m60s-vk10">M60S VK10</a></li>
<li><a href="../whatsminer/M6X#m60s-vk20">M60S VK20</a></li>
<li><a href="../whatsminer/M6X#m60s-vk30">M60S VK30</a></li>
<li><a href="../whatsminer/M6X#m60s-vk40">M60S VK40</a></li>
<li><a href="../whatsminer/M6X#m63-vk10">M63 VK10</a></li>
<li><a href="../whatsminer/M6X#m63-vk20">M63 VK20</a></li>
<li><a href="../whatsminer/M6X#m63-vk30">M63 VK30</a></li>
<li><a href="../whatsminer/M6X#m63s-vk10">M63S VK10</a></li>
<li><a href="../whatsminer/M6X#m63s-vk20">M63S VK20</a></li>
<li><a href="../whatsminer/M6X#m63s-vk30">M63S VK30</a></li>
<li><a href="../whatsminer/M6X#m66-vk20">M66 VK20</a></li>
<li><a href="../whatsminer/M6X#m66-vk30">M66 VK30</a></li>
<li><a href="../whatsminer/M6X#m66s-vk20">M66S VK20</a></li>
<li><a href="../whatsminer/M6X#m66s-vk30">M66S VK30</a></li>
<li><a href="../whatsminer/M6X#m66s-vk40">M66S VK40</a></li>
</ul>
</details>
</ul> </ul>
</details> </details>
<details> <details>
@@ -376,6 +403,13 @@ details {
<li><a href="../goldshell/XMax#kd-max">KD Max</a></li> <li><a href="../goldshell/XMax#kd-max">KD Max</a></li>
</ul> </ul>
</details> </details>
<details>
<summary>XBox Series:</summary>
<ul>
<li><a href="../goldshell/XBox#kd-box-ii">KD Box II</a></li>
<li><a href="../goldshell/XBox#kd-box-pro">KD Box Pro</a></li>
</ul>
</details>
</ul> </ul>
</details> </details>
<details> <details>
@@ -384,31 +418,37 @@ details {
<details> <details>
<summary>X9 Series:</summary> <summary>X9 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X9#s9-bos">S9 (BOS)</a></li> <li><a href="../antminer/X9#s9-bos_1">S9 (BOS+)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>X17 Series:</summary> <summary>X17 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X17#s17-bos">S17 (BOS)</a></li> <li><a href="../antminer/X17#s17-bos_1">S17 (BOS+)</a></li>
<li><a href="../antminer/X17#s17_1-bos">S17+ (BOS)</a></li> <li><a href="../antminer/X17#s17_1-bos_1">S17+ (BOS+)</a></li>
<li><a href="../antminer/X17#s17-pro-bos">S17 Pro (BOS)</a></li> <li><a href="../antminer/X17#s17-pro-bos_1">S17 Pro (BOS+)</a></li>
<li><a href="../antminer/X17#s17e-bos">S17e (BOS)</a></li> <li><a href="../antminer/X17#s17e-bos_1">S17e (BOS+)</a></li>
<li><a href="../antminer/X17#t17-bos">T17 (BOS)</a></li> <li><a href="../antminer/X17#t17-bos_1">T17 (BOS+)</a></li>
<li><a href="../antminer/X17#t17_1-bos">T17+ (BOS)</a></li> <li><a href="../antminer/X17#t17_1-bos_1">T17+ (BOS+)</a></li>
<li><a href="../antminer/X17#t17e-bos">T17e (BOS)</a></li> <li><a href="../antminer/X17#t17e-bos_1">T17e (BOS+)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>X19 Series:</summary> <summary>X19 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X19#s19-bos">S19 (BOS)</a></li> <li><a href="../antminer/X19#s19">S19</a></li>
<li><a href="../antminer/X19#s19-pro-bos">S19 Pro (BOS)</a></li> <li><a href="../antminer/X19#s19_1">S19+</a></li>
<li><a href="../antminer/X19#s19j-bos">S19j (BOS)</a></li> <li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
<li><a href="../antminer/X19#s19j-no-pic-bos">S19j No PIC (BOS)</a></li> <li><a href="../antminer/X19#s19a">S19a</a></li>
<li><a href="../antminer/X19#s19j-pro-bos">S19j Pro (BOS)</a></li> <li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li>
<li><a href="../antminer/X19#s19j-pro-bos">S19j Pro (BOS)</a></li> <li><a href="../antminer/X19#s19j">S19j</a></li>
<li><a href="../antminer/X19#t19-bos">T19 (BOS)</a></li> <li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
<li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
<li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
<li><a href="../antminer/X19#s19j-pro_1">S19j Pro+</a></li>
<li><a href="../antminer/X19#s19k-pro-no-pic">S19k Pro No PIC</a></li>
<li><a href="../antminer/X19#s19-xp">S19 XP</a></li>
<li><a href="../antminer/X19#t19">T19</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -420,6 +460,13 @@ details {
<summary>X3 Series:</summary> <summary>X3 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X3#l3_1-vnish">L3+ (VNish)</a></li> <li><a href="../antminer/X3#l3_1-vnish">L3+ (VNish)</a></li>
<li><a href="../antminer/X3#l3_1-vnish">L3+ (VNish)</a></li>
</ul>
</details>
<details>
<summary>X7 Series:</summary>
<ul>
<li><a href="../antminer/X7#l7-vnish">L7 (VNish)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
@@ -454,9 +501,23 @@ details {
<li><a href="../antminer/X19#s19-pro-epic">S19 Pro (ePIC)</a></li> <li><a href="../antminer/X19#s19-pro-epic">S19 Pro (ePIC)</a></li>
<li><a href="../antminer/X19#s19j-epic">S19j (ePIC)</a></li> <li><a href="../antminer/X19#s19j-epic">S19j (ePIC)</a></li>
<li><a href="../antminer/X19#s19j-pro-epic">S19j Pro (ePIC)</a></li> <li><a href="../antminer/X19#s19j-pro-epic">S19j Pro (ePIC)</a></li>
<li><a href="../antminer/X19#s19j-pro_1-epic">S19j Pro+ (ePIC)</a></li>
<li><a href="../antminer/X19#s19k-pro-epic">S19k Pro (ePIC)</a></li>
<li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li> <li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li>
</ul> </ul>
</details> </details>
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-epic">S21 (ePIC)</a></li>
</ul>
</details>
<details>
<summary>blockminer Series:</summary>
<ul>
<li><a href="../blockminer/blockminer#blockminer-520i-epic">BlockMiner 520i (ePIC)</a></li>
</ul>
</details>
</ul> </ul>
</details> </details>
<details> <details>
@@ -465,7 +526,7 @@ details {
<details> <details>
<summary>X9 Series:</summary> <summary>X9 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X9#t9-hiveon">T9 (Hiveon)</a></li> <li><a href="../antminer/X9#t9-hive">T9 (Hive)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -479,5 +540,38 @@ details {
<li><a href="../antminer/X9#s9-luxos">S9 (LuxOS)</a></li> <li><a href="../antminer/X9#s9-luxos">S9 (LuxOS)</a></li>
</ul> </ul>
</details> </details>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="../antminer/X19#s19-luxos">S19 (LuxOS)</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware Auradine Miners:</summary>
<ul>
<details>
<summary>AD Series:</summary>
<ul>
<li><a href="../auradine/AD#at1500">AT1500</a></li>
<li><a href="../auradine/AD#at2860">AT2860</a></li>
<li><a href="../auradine/AD#at2880">AT2880</a></li>
</ul>
</details>
<details>
<summary>AI Series:</summary>
<ul>
<li><a href="../auradine/AI#ai2500">AI2500</a></li>
<li><a href="../auradine/AI#ai3680">AI3680</a></li>
</ul>
</details>
<details>
<summary>AT Series:</summary>
<ul>
<li><a href="../auradine/AT#ad2500">AD2500</a></li>
<li><a href="../auradine/AT#ad3500">AD3500</a></li>
</ul>
</details>
</ul> </ul>
</details> </details>

View File

@@ -211,6 +211,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53S VJ40
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ40
handler: python
options:
show_root_heading: false
heading_level: 4
## M53S+ VJ30 ## M53S+ VJ30
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30 ::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30
handler: python handler: python
@@ -218,6 +225,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## M53S++ VK10
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M56 VH30 ## M56 VH30
::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30 ::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30
handler: python handler: python

View File

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

View File

@@ -40,6 +40,7 @@ nav:
- Antminer X15: "miners/antminer/X15.md" - Antminer X15: "miners/antminer/X15.md"
- Antminer X17: "miners/antminer/X17.md" - Antminer X17: "miners/antminer/X17.md"
- Antminer X19: "miners/antminer/X19.md" - Antminer X19: "miners/antminer/X19.md"
- Antminer X21: "miners/antminer/X21.md"
- Avalon 7X: "miners/avalonminer/A7X.md" - Avalon 7X: "miners/avalonminer/A7X.md"
- Avalon 8X: "miners/avalonminer/A8X.md" - Avalon 8X: "miners/avalonminer/A8X.md"
- Avalon 9X: "miners/avalonminer/A9X.md" - Avalon 9X: "miners/avalonminer/A9X.md"

View File

@@ -54,9 +54,9 @@ class FanModeNormal(MinerConfigValue):
return { return {
"fans": { "fans": {
"Auto": { "Auto": {
"Idle Speed": self.minimum_speed "Idle Speed": (
if not self.minimum_speed == 0 self.minimum_speed if not self.minimum_speed == 0 else 100
else 100 )
} }
} }
} }

View File

@@ -50,6 +50,9 @@ class MiningModeNormal(MinerConfigValue):
def as_epic(self) -> dict: def as_epic(self) -> dict:
return {"ptune": {"enabled": False}} return {"ptune": {"enabled": False}}
def as_goldshell(self) -> dict:
return {"settings": {"level": 0}}
@dataclass @dataclass
class MiningModeSleep(MinerConfigValue): class MiningModeSleep(MinerConfigValue):
@@ -71,6 +74,9 @@ class MiningModeSleep(MinerConfigValue):
def as_epic(self) -> dict: def as_epic(self) -> dict:
return {"ptune": {"algo": "Sleep", "target": 0}} return {"ptune": {"algo": "Sleep", "target": 0}}
def as_goldshell(self) -> dict:
return {"settings": {"level": 3}}
@dataclass @dataclass
class MiningModeLPM(MinerConfigValue): class MiningModeLPM(MinerConfigValue):
@@ -89,6 +95,9 @@ class MiningModeLPM(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": "eco"}} return {"mode": {"mode": "eco"}}
def as_goldshell(self) -> dict:
return {"settings": {"level": 1}}
@dataclass @dataclass
class MiningModeHPM(MinerConfigValue): class MiningModeHPM(MinerConfigValue):
@@ -108,31 +117,31 @@ class MiningModeHPM(MinerConfigValue):
return {"mode": {"mode": "turbo"}} return {"mode": {"mode": "turbo"}}
class StandardPowerTuneAlgo(MinerConfigValue): class StandardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard") mode: str = field(init=False, default="standard")
def as_epic(self): def as_epic(self) -> str:
return VOptPowerTuneAlgo().as_epic() return VOptAlgo().as_epic()
class VOptPowerTuneAlgo(MinerConfigValue): class VOptAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard") mode: str = field(init=False, default="standard")
def as_epic(self): def as_epic(self) -> str:
return "VoltageOptimizer" return "VoltageOptimizer"
class ChipTunePowerTuneAlgo(MinerConfigValue): class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard") mode: str = field(init=False, default="standard")
def as_epic(self): def as_epic(self) -> str:
return "ChipTune" return "ChipTune"
class PowerTunerAlgo(MinerConfigOption): class TunerAlgo(MinerConfigOption):
standard = StandardPowerTuneAlgo standard = StandardTuneAlgo
voltage_optimizer = VOptPowerTuneAlgo voltage_optimizer = VOptAlgo
chip_tune = ChipTunePowerTuneAlgo chip_tune = ChipTuneAlgo
@classmethod @classmethod
def default(cls): def default(cls):
@@ -143,7 +152,7 @@ class PowerTunerAlgo(MinerConfigOption):
class MiningModePowerTune(MinerConfigValue): class MiningModePowerTune(MinerConfigValue):
mode: str = field(init=False, default="power_tuning") mode: str = field(init=False, default="power_tuning")
power: int = None power: int = None
algo: PowerTunerAlgo = field(default_factory=PowerTunerAlgo.default) algo: TunerAlgo = field(default_factory=TunerAlgo.default)
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune": def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
@@ -183,14 +192,12 @@ class MiningModePowerTune(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}} return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
def as_epic(self) -> dict:
return {"ptune": {**self.algo.as_epic(), "target": self.power}}
@dataclass @dataclass
class MiningModeHashrateTune(MinerConfigValue): class MiningModeHashrateTune(MinerConfigValue):
mode: str = field(init=False, default="hashrate_tuning") mode: str = field(init=False, default="hashrate_tuning")
hashrate: int = None hashrate: int = None
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune": def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
@@ -218,6 +225,9 @@ class MiningModeHashrateTune(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}} return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
def as_epic(self) -> dict:
return {"ptune": {"algo": self.algo.as_epic(), "target": self.hashrate}}
@dataclass @dataclass
class ManualBoardSettings(MinerConfigValue): class ManualBoardSettings(MinerConfigValue):
@@ -313,14 +323,14 @@ class MiningModeConfig(MinerConfigOption):
if tuner_running: if tuner_running:
algo_info = web_conf["PerpetualTune"]["Algorithm"] algo_info = web_conf["PerpetualTune"]["Algorithm"]
if algo_info.get("VoltageOptimizer") is not None: if algo_info.get("VoltageOptimizer") is not None:
return cls.power_tuning( return cls.hashrate_tuning(
power=algo_info["VoltageOptimizer"]["Target"], hashrate=algo_info["VoltageOptimizer"]["Target"],
algo=PowerTunerAlgo.voltage_optimizer, algo=TunerAlgo.voltage_optimizer,
) )
else: else:
return cls.power_tuning( return cls.hashrate_tuning(
power=algo_info["ChipTune"]["Target"], hashrate=algo_info["ChipTune"]["Target"],
algo=PowerTunerAlgo.chip_tune, algo=TunerAlgo.chip_tune,
) )
else: else:
return cls.normal() return cls.normal()

View File

@@ -265,15 +265,7 @@ class PoolGroup(MinerConfigValue):
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools] return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
def as_epic(self, user_suffix: str = None) -> dict: def as_epic(self, user_suffix: str = None) -> dict:
if len(self.pools) > 0: return [p.as_epic(user_suffix=user_suffix) for p in self.pools]
conf = {
"name": self.name,
"pool": [pool.as_epic(user_suffix=user_suffix) for pool in self.pools],
}
if self.quota is not None:
conf["quota"] = self.quota
return conf
return {"name": self.name, "pool": []}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup": def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
@@ -335,9 +327,11 @@ class PoolGroup(MinerConfigValue):
return cls( return cls(
pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]], pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]],
name=grpc_pool_group["name"], name=grpc_pool_group["name"],
quota=grpc_pool_group["quota"]["value"] quota=(
if grpc_pool_group.get("quota") is not None grpc_pool_group["quota"]["value"]
else 1, if grpc_pool_group.get("quota") is not None
else 1
),
) )
except LookupError: except LookupError:
return cls() return cls()
@@ -421,9 +415,7 @@ class PoolConfig(MinerConfigValue):
return { return {
"pools": { "pools": {
"coin": "Btc", "coin": "Btc",
"stratum_configs": [ "stratum_configs": self.groups[0].as_epic(user_suffix=user_suffix),
g.as_epic(user_suffix=user_suffix) for g in self.groups
],
"unique_id": False, "unique_id": False,
} }
} }

View File

@@ -43,9 +43,9 @@ class TemperatureConfig(MinerConfigValue):
def as_epic(self) -> dict: def as_epic(self) -> dict:
temps_config = {"temps": {}, "fans": {"Auto": {}}} temps_config = {"temps": {}, "fans": {"Auto": {}}}
if self.target is not None: if self.target is not None:
temps_config["fans"]["Target Temperature"] = self.target temps_config["fans"]["Auto"]["Target Temperature"] = self.target
else: else:
temps_config["fans"]["Target Temperature"] = 60 temps_config["fans"]["Auto"]["Target Temperature"] = 60
if self.danger is not None: if self.danger is not None:
temps_config["temps"]["shutdown"] = self.danger temps_config["temps"]["shutdown"] = self.danger
return temps_config return temps_config

View File

@@ -94,9 +94,7 @@ class MinerData:
percent_expected_wattage: float = field(init=False) percent_expected_wattage: float = field(init=False)
nominal: bool = field(init=False) nominal: bool = field(init=False)
config: MinerConfig = None config: MinerConfig = None
errors: List[ errors: List[Union[WhatsminerError, BraiinsOSError, X19Error, InnosiliconError]] = field(default_factory=list)
Union[WhatsminerError, BraiinsOSError, X19Error, InnosiliconError]
] = field(default_factory=list)
fault_light: Union[bool, None] = None fault_light: Union[bool, None] = None
efficiency: int = field(init=False) efficiency: int = field(init=False)
is_mining: bool = True is_mining: bool = True

View File

@@ -14,21 +14,21 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSer from pyasic.miners.backends import BOSMiner
from pyasic.miners.models import S17, S17e, S17Plus, S17Pro from pyasic.miners.models import S17, S17e, S17Plus, S17Pro
class BOSMinerS17(BOSer, S17): class BOSMinerS17(BOSMiner, S17):
pass pass
class BOSMinerS17Plus(BOSer, S17Plus): class BOSMinerS17Plus(BOSMiner, S17Plus):
pass pass
class BOSMinerS17Pro(BOSer, S17Pro): class BOSMinerS17Pro(BOSMiner, S17Pro):
pass pass
class BOSMinerS17e(BOSer, S17e): class BOSMinerS17e(BOSMiner, S17e):
pass pass

View File

@@ -14,17 +14,17 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSer from pyasic.miners.backends import BOSMiner
from pyasic.miners.models import T17, T17e, T17Plus from pyasic.miners.models import T17, T17e, T17Plus
class BOSMinerT17(BOSer, T17): class BOSMinerT17(BOSMiner, T17):
pass pass
class BOSMinerT17Plus(BOSer, T17Plus): class BOSMinerT17Plus(BOSMiner, T17Plus):
pass pass
class BOSMinerT17e(BOSer, T17e): class BOSMinerT17e(BOSMiner, T17e):
pass 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 ePIC
from pyasic.miners.models import S21
class ePICS21(ePIC, S21):
pass

View File

@@ -0,0 +1,19 @@
# ------------------------------------------------------------------------------
# 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 .S21 import (
ePICS21,
)

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .X19 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 LUXMiner
from pyasic.miners.models import S19
class LUXMinerS19(LUXMiner, S19):
pass

View File

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

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .X9 import * from .X9 import *
from .X19 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 VNish
from pyasic.miners.models import L7
class VnishL7(VNish, L7):
pass

View File

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

View File

@@ -15,5 +15,6 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .X3 import * from .X3 import *
from .X7 import *
from .X17 import * from .X17 import *
from .X19 import * from .X19 import *

View File

@@ -94,6 +94,7 @@ class AntminerModern(BMMiner):
data_locations = ANTMINER_MODERN_DATA_LOC data_locations = ANTMINER_MODERN_DATA_LOC
supports_shutdown = True supports_shutdown = True
supports_power_modes = True
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
data = await self.web.get_miner_conf() data = await self.web.get_miner_conf()
@@ -206,7 +207,7 @@ class AntminerModern(BMMiner):
] ]
try: try:
rpc_stats = await self.rpc.send_command("stats", new_rpc=True) rpc_stats = await self.rpc.send_command("stats", new_api=True)
except APIError: except APIError:
return hashboards return hashboards

View File

@@ -124,6 +124,7 @@ class Auradine(BaseMiner):
data_locations = AURADINE_DATA_LOC data_locations = AURADINE_DATA_LOC
supports_shutdown = True supports_shutdown = True
supports_power_modes = True
supports_autotuning = True supports_autotuning = True
async def fault_light_on(self) -> bool: async def fault_light_on(self) -> bool:

View File

@@ -68,7 +68,7 @@ class BFGMiner(BaseMiner):
except APIError: except APIError:
return self.config return self.config
self.config = MinerConfig.from_rpc(pools) self.config = MinerConfig.from_api(pools)
return self.config return self.config
################################################## ##################################################
@@ -84,11 +84,11 @@ class BFGMiner(BaseMiner):
if rpc_version is not None: if rpc_version is not None:
try: try:
self.rpc_ver = rpc_version["VERSION"][0]["API"] self.api_ver = rpc_version["VERSION"][0]["API"]
except LookupError: except LookupError:
pass pass
return self.rpc_ver return self.api_ver
async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]: async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None: if rpc_version is None:

View File

@@ -72,7 +72,7 @@ class BMMiner(BaseMiner):
except APIError: except APIError:
return self.config return self.config
self.config = MinerConfig.from_rpc(pools) self.config = MinerConfig.from_api(pools)
return self.config return self.config
################################################## ##################################################
@@ -88,11 +88,11 @@ class BMMiner(BaseMiner):
if rpc_version is not None: if rpc_version is not None:
try: try:
self.rpc_ver = rpc_version["VERSION"][0]["API"] self.api_ver = rpc_version["VERSION"][0]["API"]
except LookupError: except LookupError:
pass pass
return self.rpc_ver return self.api_ver
async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]: async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None: if rpc_version is None:

View File

@@ -35,6 +35,7 @@ from pyasic.miners.data import (
from pyasic.rpc.bosminer import BOSMinerRPCAPI from pyasic.rpc.bosminer import BOSMinerRPCAPI
from pyasic.ssh.braiins_os import BOSMinerSSH from pyasic.ssh.braiins_os import BOSMinerSSH
from pyasic.web.braiins_os import BOSerWebAPI, BOSMinerWebAPI from pyasic.web.braiins_os import BOSerWebAPI, BOSMinerWebAPI
from pyasic.web.braiins_os.proto.braiins.bos.v1 import SaveAction
BOSMINER_DATA_LOC = DataLocations( BOSMINER_DATA_LOC = DataLocations(
**{ **{
@@ -305,10 +306,10 @@ class BOSMiner(BaseMiner):
rpc_ver = rpc_version["VERSION"][0]["API"] rpc_ver = rpc_version["VERSION"][0]["API"]
except LookupError: except LookupError:
rpc_ver = None rpc_ver = None
self.rpc_ver = rpc_ver self.api_ver = rpc_ver
self.rpc.rpc_ver = self.rpc_ver self.rpc.rpc_ver = self.api_ver
return self.rpc_ver return self.api_ver
async def _get_fw_ver(self, web_bos_info: dict = None) -> Optional[str]: async def _get_fw_ver(self, web_bos_info: dict = None) -> Optional[str]:
if web_bos_info is None: if web_bos_info is None:
@@ -691,7 +692,9 @@ class BOSer(BaseMiner):
async def set_power_limit(self, wattage: int) -> bool: async def set_power_limit(self, wattage: int) -> bool:
try: try:
result = await self.web.set_power_target(wattage) result = await self.web.set_power_target(
wattage, save_action=SaveAction.SAVE_ACTION_SAVE_AND_FORCE_APPLY
)
except APIError: except APIError:
return False return False
@@ -731,10 +734,10 @@ class BOSer(BaseMiner):
rpc_ver = rpc_version["VERSION"][0]["API"] rpc_ver = rpc_version["VERSION"][0]["API"]
except LookupError: except LookupError:
rpc_ver = None rpc_ver = None
self.rpc_ver = rpc_ver self.api_ver = rpc_ver
self.rpc.rpc_ver = self.rpc_ver self.rpc.rpc_ver = self.api_ver
return self.rpc_ver return self.api_ver
async def _get_fw_ver(self, grpc_miner_details: dict = None) -> Optional[str]: async def _get_fw_ver(self, grpc_miner_details: dict = None) -> Optional[str]:
if grpc_miner_details is None: if grpc_miner_details is None:

View File

@@ -119,6 +119,7 @@ class BTMiner(BaseMiner):
data_locations = BTMINER_DATA_LOC data_locations = BTMINER_DATA_LOC
supports_shutdown = True supports_shutdown = True
supports_power_modes = True
async def _reset_rpc_pwd_to_admin(self, pwd: str): async def _reset_rpc_pwd_to_admin(self, pwd: str):
try: try:
@@ -234,7 +235,7 @@ class BTMiner(BaseMiner):
pass pass
if pools is not None: if pools is not None:
cfg = MinerConfig.from_rpc(pools) cfg = MinerConfig.from_api(pools)
else: else:
cfg = MinerConfig() cfg = MinerConfig()
@@ -325,14 +326,14 @@ class BTMiner(BaseMiner):
rpc_ver = rpc_get_version["Msg"] rpc_ver = rpc_get_version["Msg"]
if not isinstance(rpc_ver, str): if not isinstance(rpc_ver, str):
rpc_ver = rpc_ver["rpc_ver"] rpc_ver = rpc_ver["rpc_ver"]
self.rpc_ver = rpc_ver.replace("whatsminer v", "") self.api_ver = rpc_ver.replace("whatsminer v", "")
except (KeyError, TypeError): except (KeyError, TypeError):
pass pass
else: else:
self.rpc.rpc_ver = self.rpc_ver self.rpc.rpc_ver = self.api_ver
return self.rpc_ver return self.api_ver
return self.rpc_ver return self.api_ver
async def _get_fw_ver( async def _get_fw_ver(
self, rpc_get_version: dict = None, rpc_summary: dict = None self, rpc_get_version: dict = None, rpc_summary: dict = None

View File

@@ -71,7 +71,7 @@ class CGMiner(BaseMiner):
except APIError: except APIError:
return self.config return self.config
self.config = MinerConfig.from_rpc(pools) self.config = MinerConfig.from_api(pools)
return self.config return self.config
################################################## ##################################################
@@ -87,11 +87,11 @@ class CGMiner(BaseMiner):
if rpc_version is not None: if rpc_version is not None:
try: try:
self.rpc_ver = rpc_version["VERSION"][0]["API"] self.api_ver = rpc_version["VERSION"][0]["API"]
except LookupError: except LookupError:
pass pass
return self.rpc_ver return self.api_ver
async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]: async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None: if rpc_version is None:

View File

@@ -51,7 +51,7 @@ EPIC_DATA_LOC = DataLocations(
"_get_hashboards", "_get_hashboards",
[ [
WebAPICommand("web_summary", "summary"), WebAPICommand("web_summary", "summary"),
WebAPICommand("web_hashrate", "hashrate"), WebAPICommand("web_capabilities", "capabilities"),
], ],
), ),
str(DataOptions.WATTAGE): DataFunction( str(DataOptions.WATTAGE): DataFunction(
@@ -116,6 +116,7 @@ class ePIC(BaseMiner):
if not conf.get("temps", {}) == {}: if not conf.get("temps", {}) == {}:
await self.web.set_shutdown_temp(conf["temps"]["shutdown"]) await self.web.set_shutdown_temp(conf["temps"]["shutdown"])
# Fans # Fans
# set with sub-keys instead of conf["fans"] because sometimes both can be set
if not conf["fans"].get("Manual", {}) == {}: if not conf["fans"].get("Manual", {}) == {}:
await self.web.set_fan({"Manual": conf["fans"]["Manual"]}) await self.web.set_fan({"Manual": conf["fans"]["Manual"]})
elif not conf["fans"].get("Auto", {}) == {}: elif not conf["fans"].get("Auto", {}) == {}:
@@ -283,7 +284,7 @@ class ePIC(BaseMiner):
return fans return fans
async def _get_hashboards( async def _get_hashboards(
self, web_summary: dict = None, web_hashrate: dict = None self, web_summary: dict = None, web_capabilities: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if web_summary is None: if web_summary is None:
try: try:
@@ -291,28 +292,25 @@ class ePIC(BaseMiner):
except APIError: except APIError:
pass pass
if web_hashrate is not None: if web_capabilities is not None:
try: try:
web_hashrate = await self.web.hashrate() web_capabilities = await self.web.capabilities()
except APIError: except APIError:
pass pass
hb_list = [ hb_list = [
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(self.expected_hashboards)
] ]
if web_summary.get("HBs") is not None: if web_summary.get("HBs") is not None:
for hb in web_summary["HBs"]: for hb in web_summary["HBs"]:
for hr in web_hashrate: num_of_chips = web_capabilities["Performance Estimator"]["Chip Count"]
if hr["Index"] == hb["Index"]: hashrate = hb["Hashrate"][0]
num_of_chips = len(hr["Data"]) # Update the Hashboard object
hashrate = hb["Hashrate"][0] hb_list[hb["Index"]].missing = False
# Update the Hashboard object hb_list[hb["Index"]].hashrate = round(hashrate / 1000000, 2)
hb_list[hr["Index"]].expected_chips = num_of_chips hb_list[hb["Index"]].chips = num_of_chips
hb_list[hr["Index"]].missing = False hb_list[hb["Index"]].temp = hb["Temperature"]
hb_list[hr["Index"]].hashrate = round(hashrate / 1000000, 2)
hb_list[hr["Index"]].chips = num_of_chips
hb_list[hr["Index"]].temp = hb["Temperature"]
return hb_list return hb_list
async def _is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:

View File

@@ -15,7 +15,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from typing import List from typing import List
from pyasic.config import MinerConfig from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import HashBoard from pyasic.data import HashBoard
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger from pyasic.logger import logger
@@ -74,6 +74,9 @@ class GoldshellMiner(BFGMiner):
data_locations = GOLDSHELL_DATA_LOC data_locations = GOLDSHELL_DATA_LOC
supports_shutdown = True
supports_power_modes = True
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
# get pool data # get pool data
try: try:
@@ -96,13 +99,19 @@ class GoldshellMiner(BFGMiner):
) )
self.config = config self.config = config
cfg = config.as_goldshell(user_suffix=user_suffix)
# send them back 1 at a time # send them back 1 at a time
for pool in config.as_goldshell(user_suffix=user_suffix)["pools"]: for pool in cfg["pools"]:
await self.web.newpool( await self.web.newpool(
url=pool["url"], user=pool["user"], password=pool["pass"] url=pool["url"], user=pool["user"], password=pool["pass"]
) )
settings = await self.web.setting()
for idx, plan in enumerate(settings["powerplans"]):
if plan["level"] == cfg["settings"]["level"]:
settings["select"] = idx
await self.web.set_setting(settings)
async def _get_mac(self, web_setting: dict = None) -> str: async def _get_mac(self, web_setting: dict = None) -> str:
if web_setting is None: if web_setting is None:
try: try:
@@ -178,3 +187,25 @@ class GoldshellMiner(BFGMiner):
logger.error(self, rpc_devdetails) logger.error(self, rpc_devdetails)
return hashboards return hashboards
async def stop_mining(self) -> bool:
settings = await self.web.setting()
mode = MiningModeConfig.sleep()
cfg = mode.as_goldshell()
level = cfg["settings"]["level"]
for idx, plan in enumerate(settings["powerplans"]):
if plan["level"] == level:
settings["select"] = idx
await self.web.set_setting(settings)
return True
async def resume_mining(self) -> bool:
settings = await self.web.setting()
mode = MiningModeConfig.normal()
cfg = mode.as_goldshell()
level = cfg["settings"]["level"]
for idx, plan in enumerate(settings["powerplans"]):
if plan["level"] == level:
settings["select"] = idx
await self.web.set_setting(settings)
return True

View File

@@ -205,13 +205,14 @@ class VNish(BMMiner):
if web_summary is None: if web_summary is None:
web_summary = await self.web.summary() web_summary = await self.web.summary()
fw_ver = None
if web_summary is not None: if web_summary is not None:
try: try:
fw_ver = web_summary["miner"]["miner_type"] fw_ver = web_summary["miner"]["miner_type"]
fw_ver = fw_ver.split("(Vnish ")[1].replace(")", "") fw_ver = fw_ver.split("(Vnish ")[1].replace(")", "")
return fw_ver return fw_ver
except KeyError: except LookupError:
pass return fw_ver
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
try: try:

View File

@@ -47,6 +47,7 @@ class MinerProtocol(Protocol):
data_locations: DataLocations = None data_locations: DataLocations = None
supports_shutdown: bool = False supports_shutdown: bool = False
supports_power_modes: bool = False
supports_autotuning: bool = False supports_autotuning: bool = False
api_ver: str = None api_ver: str = None
@@ -68,7 +69,12 @@ class MinerProtocol(Protocol):
@property @property
def model(self) -> str: def model(self) -> str:
model_data = [self.raw_model if self.raw_model is not None else "Unknown"] if self.raw_model is not None:
model_data = [self.raw_model]
elif self.make is not None:
model_data = [self.make]
else:
model_data = ["Unknown"]
if self.firmware is not None: if self.firmware is not None:
model_data.append(f"({self.firmware})") model_data.append(f"({self.firmware})")
return " ".join(model_data) return " ".join(model_data)
@@ -461,9 +467,11 @@ class MinerProtocol(Protocol):
ip=str(self.ip), ip=str(self.ip),
make=self.make, make=self.make,
model=self.model, model=self.model,
expected_chips=self.expected_chips * self.expected_hashboards expected_chips=(
if self.expected_chips is not None self.expected_chips * self.expected_hashboards
else 0, if self.expected_chips is not None
else 0
),
expected_hashboards=self.expected_hashboards, expected_hashboards=self.expected_hashboards,
hashboards=[ hashboards=[
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)

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 .epic import *

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 .blockminer import *

View File

@@ -0,0 +1,19 @@
# ------------------------------------------------------------------------------
# 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 .blockminer import (
ePICBlockMiner520i,
)

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 ePIC
from pyasic.miners.models import BlockMiner520i
class ePICBlockMiner520i(ePIC, BlockMiner520i):
pass

View File

@@ -29,6 +29,7 @@ from pyasic import settings
from pyasic.logger import logger from pyasic.logger import logger
from pyasic.miners.antminer import * from pyasic.miners.antminer import *
from pyasic.miners.auradine import * from pyasic.miners.auradine import *
from pyasic.miners.blockminer import *
from pyasic.miners.avalonminer import * from pyasic.miners.avalonminer import *
from pyasic.miners.backends import ( from pyasic.miners.backends import (
Auradine, Auradine,
@@ -47,6 +48,7 @@ from pyasic.miners.backends.unknown import UnknownMiner
from pyasic.miners.base import AnyMiner from pyasic.miners.base import AnyMiner
from pyasic.miners.goldshell import * from pyasic.miners.goldshell import *
from pyasic.miners.innosilicon import * from pyasic.miners.innosilicon import *
from pyasic.miners.makes import *
from pyasic.miners.whatsminer import * from pyasic.miners.whatsminer import *
@@ -66,7 +68,7 @@ class MinerTypes(enum.Enum):
MINER_CLASSES = { MINER_CLASSES = {
MinerTypes.ANTMINER: { MinerTypes.ANTMINER: {
None: BMMiner, None: type("AntminerUnknown", (BMMiner, AntMinerMake), {}),
"ANTMINER D3": CGMinerD3, "ANTMINER D3": CGMinerD3,
"ANTMINER HS3": BMMinerHS3, "ANTMINER HS3": BMMinerHS3,
"ANTMINER L3+": BMMinerL3Plus, "ANTMINER L3+": BMMinerL3Plus,
@@ -101,7 +103,7 @@ MINER_CLASSES = {
"ANTMINER T19": BMMinerT19, "ANTMINER T19": BMMinerT19,
}, },
MinerTypes.WHATSMINER: { MinerTypes.WHATSMINER: {
None: BTMiner, None: type("WhatsminerUnknown", (BTMiner, WhatsMinerMake), {}),
"M20V10": BTMinerM20V10, "M20V10": BTMinerM20V10,
"M20SV10": BTMinerM20SV10, "M20SV10": BTMinerM20SV10,
"M20SV20": BTMinerM20SV20, "M20SV20": BTMinerM20SV20,
@@ -289,7 +291,9 @@ MINER_CLASSES = {
"M50S++VK30": BTMinerM50SPlusPlusVK30, "M50S++VK30": BTMinerM50SPlusPlusVK30,
"M53VH30": BTMinerM53VH30, "M53VH30": BTMinerM53VH30,
"M53SVH30": BTMinerM53SVH30, "M53SVH30": BTMinerM53SVH30,
"M53SVJ40": BTMinerM53SVJ40,
"M53S+VJ30": BTMinerM53SPlusVJ30, "M53S+VJ30": BTMinerM53SPlusVJ30,
"M53S++VK10": BTMinerM53SPlusPlusVK10,
"M56VH30": BTMinerM56VH30, "M56VH30": BTMinerM56VH30,
"M56SVH30": BTMinerM56SVH30, "M56SVH30": BTMinerM56SVH30,
"M56S+VJ30": BTMinerM56SPlusVJ30, "M56S+VJ30": BTMinerM56SPlusVJ30,
@@ -315,7 +319,7 @@ MINER_CLASSES = {
"M66SVK40": BTMinerM66SVK40, "M66SVK40": BTMinerM66SVK40,
}, },
MinerTypes.AVALONMINER: { MinerTypes.AVALONMINER: {
None: AvalonMiner, None: type("AvalonUnknown", (AvalonMiner, AvalonMinerMake), {}),
"AVALONMINER 721": CGMinerAvalon721, "AVALONMINER 721": CGMinerAvalon721,
"AVALONMINER 741": CGMinerAvalon741, "AVALONMINER 741": CGMinerAvalon741,
"AVALONMINER 761": CGMinerAvalon761, "AVALONMINER 761": CGMinerAvalon761,
@@ -330,16 +334,18 @@ MINER_CLASSES = {
"AVALONMINER 1246": CGMinerAvalon1246, "AVALONMINER 1246": CGMinerAvalon1246,
}, },
MinerTypes.INNOSILICON: { MinerTypes.INNOSILICON: {
None: Innosilicon, None: type("InnosiliconUnknown", (Innosilicon, InnosiliconMake), {}),
"T3H+": InnosiliconT3HPlus, "T3H+": InnosiliconT3HPlus,
"A10X": InnosiliconA10X, "A10X": InnosiliconA10X,
}, },
MinerTypes.GOLDSHELL: { MinerTypes.GOLDSHELL: {
None: GoldshellMiner, None: type("GoldshellUnknown", (GoldshellMiner, GoldshellMake), {}),
"GOLDSHELL CK5": GoldshellCK5, "GOLDSHELL CK5": GoldshellCK5,
"GOLDSHELL HS5": GoldshellHS5, "GOLDSHELL HS5": GoldshellHS5,
"GOLDSHELL KD5": GoldshellKD5, "GOLDSHELL KD5": GoldshellKD5,
"GOLDSHELL KDMAX": GoldshellKDMax, "GOLDSHELL KDMAX": GoldshellKDMax,
"GOLDSHELL KDBOXII": GoldshellKDBoxII,
"GOLDSHELL KDBOXPRO": GoldshellKDBoxPro,
}, },
MinerTypes.BRAIINS_OS: { MinerTypes.BRAIINS_OS: {
None: BOSMiner, None: BOSMiner,
@@ -367,7 +373,9 @@ MINER_CLASSES = {
}, },
MinerTypes.VNISH: { MinerTypes.VNISH: {
None: VNish, None: VNish,
"L3+": VnishL3Plus,
"ANTMINER L3+": VnishL3Plus, "ANTMINER L3+": VnishL3Plus,
"ANTMINER L7": VnishL7,
"ANTMINER S17+": VNishS17Plus, "ANTMINER S17+": VNishS17Plus,
"ANTMINER S17 PRO": VNishS17Pro, "ANTMINER S17 PRO": VNishS17Pro,
"ANTMINER S19": VNishS19, "ANTMINER S19": VNishS19,
@@ -388,6 +396,8 @@ MINER_CLASSES = {
"ANTMINER S19J PRO+": ePICS19jProPlus, "ANTMINER S19J PRO+": ePICS19jProPlus,
"ANTMINER S19K PRO": ePICS19kPro, "ANTMINER S19K PRO": ePICS19kPro,
"ANTMINER S19 XP": ePICS19XP, "ANTMINER S19 XP": ePICS19XP,
"ANTMINER S21": ePICS21,
"BLOCKMINER 520I": ePICBlockMiner520i,
}, },
MinerTypes.HIVEON: { MinerTypes.HIVEON: {
None: Hiveon, None: Hiveon,
@@ -396,9 +406,10 @@ MINER_CLASSES = {
MinerTypes.LUX_OS: { MinerTypes.LUX_OS: {
None: LUXMiner, None: LUXMiner,
"ANTMINER S9": LUXMinerS9, "ANTMINER S9": LUXMinerS9,
"ANTMINER S19": LUXMinerS19,
}, },
MinerTypes.AURADINE: { MinerTypes.AURADINE: {
None: Auradine, None: type("GoldshellUnknown", (Auradine, AuradineMake), {}),
"AT1500": AuradineFluxAT1500, "AT1500": AuradineFluxAT1500,
"AT2860": AuradineFluxAT2860, "AT2860": AuradineFluxAT2860,
"AT2880": AuradineFluxAT2880, "AT2880": AuradineFluxAT2880,
@@ -494,7 +505,6 @@ class MinerFactory:
) )
except asyncio.TimeoutError: except asyncio.TimeoutError:
pass pass
miner = self._select_miner_from_classes( miner = self._select_miner_from_classes(
ip, ip,
miner_type=miner_type, miner_type=miner_type,
@@ -666,7 +676,11 @@ class MinerFactory:
return MinerTypes.LUX_OS return MinerTypes.LUX_OS
if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data: if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data:
return MinerTypes.ANTMINER return MinerTypes.ANTMINER
if "INTCHAINS_QOMO" in upper_data: if (
"INTCHAINS_QOMO" in upper_data
or "KDAMINER" in upper_data
or "BFGMINER" in upper_data
):
return MinerTypes.GOLDSHELL return MinerTypes.GOLDSHELL
if "AVALON" in upper_data: if "AVALON" in upper_data:
return MinerTypes.AVALONMINER return MinerTypes.AVALONMINER
@@ -930,12 +944,16 @@ class MinerFactory:
pass pass
async def get_miner_model_epic(self, ip: str) -> str | None: async def get_miner_model_epic(self, ip: str) -> str | None:
sock_json_data = await self.send_web_command(ip, ":4028/capabilities") for retry_cnt in range(settings.get("get_data_retries", 1)):
try: sock_json_data = await self.send_web_command(ip, ":4028/capabilities")
miner_model = sock_json_data["Model"] try:
return miner_model miner_model = sock_json_data["Model"]
except (TypeError, LookupError): return miner_model
pass except (TypeError, LookupError):
if retry_cnt < settings.get("get_data_retries", 1) - 1:
continue
else:
pass
async def get_miner_model_hiveon(self, ip: str) -> str | None: async def get_miner_model_hiveon(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "version") sock_json_data = await self.send_api_command(ip, "version")
@@ -968,6 +986,7 @@ class MinerFactory:
miner_factory = MinerFactory() miner_factory = MinerFactory()
# abstracted version of get miner that is easier to access # abstracted version of get miner that is easier to access
async def get_miner(ip: ipaddress.ip_address | str) -> AnyMiner: async def get_miner(ip: ipaddress.ip_address | str) -> AnyMiner:
return await miner_factory.get_miner(ip) return await miner_factory.get_miner(ip)

View File

@@ -0,0 +1,25 @@
# ------------------------------------------------------------------------------
# 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 GoldshellMiner
from pyasic.miners.models import KDBoxII, KDBoxPro
class GoldshellKDBoxII(GoldshellMiner, KDBoxII):
pass
class GoldshellKDBoxPro(GoldshellMiner, KDBoxPro):
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 .KDBox import GoldshellKDBoxII, GoldshellKDBoxPro

View File

@@ -14,4 +14,5 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .X5 import * from .X5 import *
from .XBox import *
from .XMax import * from .XMax import *

View File

@@ -39,3 +39,7 @@ class GoldshellMake(BaseMiner):
class AuradineMake(BaseMiner): class AuradineMake(BaseMiner):
make = "Auradine" make = "Auradine"
class ePICMake(BaseMiner):
make = "ePIC"

View File

@@ -20,3 +20,4 @@ from .avalonminer import *
from .goldshell import * from .goldshell import *
from .innosilicon import * from .innosilicon import *
from .whatsminer import * from .whatsminer import *
from .epic import *

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.miners.makes import AntMinerMake
class S21(AntMinerMake):
raw_model = "S21"
expected_chips = 108
expected_fans = 4

View File

@@ -0,0 +1,19 @@
# ------------------------------------------------------------------------------
# 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 .S21 import (
S21,
)

View File

@@ -20,3 +20,4 @@ from .X9 import *
from .X15 import * from .X15 import *
from .X17 import * from .X17 import *
from .X19 import * from .X19 import *
from .X21 import *

View File

@@ -0,0 +1 @@
from .blockminer import *

View File

@@ -0,0 +1 @@
from .blockminer import *

View File

@@ -0,0 +1,7 @@
from pyasic.miners.makes import ePICMake
class BlockMiner520i(ePICMake):
raw_model = "BlockMiner 520i"
expected_chips = 124
expected_fans = 4

View File

@@ -0,0 +1,30 @@
# ------------------------------------------------------------------------------
# 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.makes import GoldshellMake
class KDBoxII(GoldshellMake):
raw_model = "KD Box II"
expected_chips = 36
expected_fans = 2
expected_hashboards = 1
class KDBoxPro(GoldshellMake):
raw_model = "KD Box Pro"
expected_chips = 16
expected_fans = 2
expected_hashboards = 1

View File

@@ -0,0 +1 @@
from .KDBox import KDBoxII, KDBoxPro

View File

@@ -14,4 +14,5 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .X5 import * from .X5 import *
from .XBox import *
from .XMax import * from .XMax import *

View File

@@ -20,3 +20,8 @@ from pyasic.miners.makes import WhatsMinerMake
class M53SVH30(WhatsMinerMake): class M53SVH30(WhatsMinerMake):
raw_model = "M53S VH30" raw_model = "M53S VH30"
expected_fans = 0 expected_fans = 0
class M53SVJ40(WhatsMinerMake):
raw_model = "M53S VJ40"
expected_fans = 0

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.makes import WhatsMinerMake
class M53SPlusPlusVK10(WhatsMinerMake):
raw_model = "M53S++ VK10"
expected_fans = 0

View File

@@ -42,8 +42,9 @@ from .M50S import (
from .M50S_Plus import M50SPlusVH30, M50SPlusVH40, M50SPlusVJ30, M50SPlusVK20 from .M50S_Plus import M50SPlusVH30, M50SPlusVH40, M50SPlusVJ30, M50SPlusVK20
from .M50S_Plus_Plus import M50SPlusPlusVK10, M50SPlusPlusVK20, M50SPlusPlusVK30 from .M50S_Plus_Plus import M50SPlusPlusVK10, M50SPlusPlusVK20, M50SPlusPlusVK30
from .M53 import M53VH30 from .M53 import M53VH30
from .M53S import M53SVH30 from .M53S import M53SVH30, M53SVJ40
from .M53S_Plus import M53SPlusVJ30 from .M53S_Plus import M53SPlusVJ30
from .M53S_Plus_Plus import M53SPlusPlusVK10
from .M56 import M56VH30 from .M56 import M56VH30
from .M56S import M56SVH30 from .M56S import M56SVH30
from .M56S_Plus import M56SPlusVJ30 from .M56S_Plus import M56SPlusVJ30

View File

@@ -15,8 +15,12 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import M5X from pyasic.miners.backends import M5X
from pyasic.miners.models import M53SVH30 from pyasic.miners.models import M53SVH30, M53SVJ40
class BTMinerM53SVH30(M5X, M53SVH30): class BTMinerM53SVH30(M5X, M53SVH30):
pass pass
class BTMinerM53SVJ40(M5X, M53SVJ40):
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 M5X
from pyasic.miners.models import M53SPlusPlusVK10
class BTMinerM53SPlusPlusVK10(M5X, M53SPlusPlusVK10):
pass

View File

@@ -51,8 +51,9 @@ from .M50S_Plus_Plus import (
BTMinerM50SPlusPlusVK30, BTMinerM50SPlusPlusVK30,
) )
from .M53 import BTMinerM53VH30 from .M53 import BTMinerM53VH30
from .M53S import BTMinerM53SVH30 from .M53S import BTMinerM53SVH30, BTMinerM53SVJ40
from .M53S_Plus import BTMinerM53SPlusVJ30 from .M53S_Plus import BTMinerM53SPlusVJ30
from .M53S_Plus_Plus import BTMinerM53SPlusPlusVK10
from .M56 import BTMinerM56VH30 from .M56 import BTMinerM56VH30
from .M56S import BTMinerM56SVH30 from .M56S import BTMinerM56SVH30
from .M56S_Plus import BTMinerM56SPlusVJ30 from .M56S_Plus import BTMinerM56SPlusVJ30

View File

@@ -107,3 +107,4 @@ def validate_command_output(data: dict) -> tuple[bool, str | None]:
if data[key][0]["STATUS"][0]["STATUS"] not in ["S", "I"]: if data[key][0]["STATUS"][0]["STATUS"] not in ["S", "I"]:
# this is an error # this is an error
return False, f"{key}: " + data[key][0]["STATUS"][0]["Msg"] return False, f"{key}: " + data[key][0]["STATUS"][0]["Msg"]
return True, None

View File

@@ -78,6 +78,9 @@ class BaseMinerRPCAPI:
# send the command # send the command
data = await self._send_bytes(json.dumps(cmd).encode("utf-8")) data = await self._send_bytes(json.dumps(cmd).encode("utf-8"))
if data is None:
raise APIError("No data returned from the API.")
if data == b"Socket connect failed: Connection refused\n": if data == b"Socket connect failed: Connection refused\n":
if not ignore_errors: if not ignore_errors:
raise APIError(data.decode("utf-8")) raise APIError(data.decode("utf-8"))
@@ -90,7 +93,7 @@ class BaseMinerRPCAPI:
if not validation[0]: if not validation[0]:
if not ignore_errors: if not ignore_errors:
# validate the command succeeded # validate the command succeeded
raise APIError(validation[1]) raise APIError(f"{command}: {validation[1]}")
if allow_warning: if allow_warning:
logging.warning( logging.warning(
f"{self.ip}: API Command Error: {command}: {validation[1]}" f"{self.ip}: API Command Error: {command}: {validation[1]}"
@@ -162,7 +165,7 @@ class BaseMinerRPCAPI:
for func in for func in
# each function in self # each function in self
dir(self) dir(self)
if not func == "commands" if not func in ["commands", "open_api"]
if callable(getattr(self, func)) and if callable(getattr(self, func)) and
# no __ or _ methods # no __ or _ methods
not func.startswith("__") and not func.startswith("_") and not func.startswith("__") and not func.startswith("_") and
@@ -193,12 +196,16 @@ If you are sure you want to use this command please use API.send_command("{comma
async def _send_bytes( async def _send_bytes(
self, self,
data: bytes, data: bytes,
*,
port: int = None,
timeout: int = 100, timeout: int = 100,
) -> bytes: ) -> bytes:
if port is None:
port = self.port
logging.debug(f"{self} - ([Hidden] Send Bytes) - Sending") logging.debug(f"{self} - ([Hidden] Send Bytes) - Sending")
try: try:
# get reader and writer streams # get reader and writer streams
reader, writer = await asyncio.open_connection(str(self.ip), self.port) reader, writer = await asyncio.open_connection(str(self.ip), port)
# handle OSError 121 # handle OSError 121
except OSError as e: except OSError as e:
if e.errno == 121: if e.errno == 121:
@@ -208,39 +215,14 @@ If you are sure you want to use this command please use API.send_command("{comma
return b"{}" return b"{}"
# send the command # send the command
data_task = asyncio.create_task(self._read_bytes(reader, timeout=timeout))
logging.debug(f"{self} - ([Hidden] Send Bytes) - Writing") logging.debug(f"{self} - ([Hidden] Send Bytes) - Writing")
writer.write(data) writer.write(data)
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining") logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
await writer.drain() await writer.drain()
try:
# TO address a situation where a whatsminer has an unknown PW -AND-
# Fix for stupid whatsminer bug, reboot/restart seem to not load properly in the loop
# have to receive, save the data, check if there is more data by reading with a short timeout
# append that data if there is more, and then onto the main loop.
# the password timeout might need to be longer than 1, but it seems to work for now.
ret_data = await asyncio.wait_for(reader.read(1), timeout=1)
except asyncio.TimeoutError:
return b"{}"
try:
ret_data += await asyncio.wait_for(reader.read(4096), timeout=timeout)
except ConnectionAbortedError:
return b"{}"
# loop to receive all the data await data_task
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving") ret_data = data_task.result()
try:
while True:
try:
d = await asyncio.wait_for(reader.read(4096), timeout=timeout)
if not d:
break
ret_data += d
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
raise e
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
raise e
except Exception as e:
logging.warning(f"{self} - ([Hidden] Send Bytes) - API Command Error {e}")
# close the connection # close the connection
logging.debug(f"{self} - ([Hidden] Send Bytes) - Closing") logging.debug(f"{self} - ([Hidden] Send Bytes) - Closing")
@@ -249,6 +231,19 @@ If you are sure you want to use this command please use API.send_command("{comma
return ret_data return ret_data
async def _read_bytes(self, reader: asyncio.StreamReader, timeout: int) -> bytes:
ret_data = b""
# loop to receive all the data
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving")
try:
ret_data = await asyncio.wait_for(reader.read(), timeout=timeout)
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
raise e
except Exception as e:
logging.warning(f"{self} - ([Hidden] Send Bytes) - API Command Error {e}")
return ret_data
@staticmethod @staticmethod
def _load_api_data(data: bytes) -> dict: def _load_api_data(data: bytes) -> dict:
# some json from the API returns with a null byte (\x00) on the end # some json from the API returns with a null byte (\x00) on the end

View File

@@ -24,12 +24,13 @@ import logging
import re import re
from typing import Literal, Union from typing import Literal, Union
import httpx
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from passlib.handlers.md5_crypt import md5_crypt from passlib.handlers.md5_crypt import md5_crypt
from pyasic import settings from pyasic import settings
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.misc import api_min_version from pyasic.misc import api_min_version, validate_command_output
from pyasic.rpc.base import BaseMinerRPCAPI from pyasic.rpc.base import BaseMinerRPCAPI
### IMPORTANT ### ### IMPORTANT ###
@@ -240,6 +241,28 @@ class BTMinerRPCAPI(BaseMinerRPCAPI):
ignore_errors: bool = False, ignore_errors: bool = False,
timeout: int = 10, timeout: int = 10,
**kwargs, **kwargs,
) -> dict:
try:
return await self._send_privileged_command(
command=command, ignore_errors=ignore_errors, timeout=timeout, **kwargs
)
except APIError as e:
if not e.message == "can't access write cmd":
raise
try:
await self.open_api()
except Exception as e:
raise APIError("Failed to open whatsminer API.") from e
return await self._send_privileged_command(
command=command, ignore_errors=ignore_errors, timeout=timeout, **kwargs
)
async def _send_privileged_command(
self,
command: Union[str, bytes],
ignore_errors: bool = False,
timeout: int = 10,
**kwargs,
) -> dict: ) -> dict:
logging.debug( logging.debug(
f"{self} - (Send Privileged Command) - {command} " + f"with args {kwargs}" f"{self} - (Send Privileged Command) - {command} " + f"with args {kwargs}"
@@ -253,7 +276,7 @@ class BTMinerRPCAPI(BaseMinerRPCAPI):
logging.debug(f"{self} - (Send Privileged Command) - Sending") logging.debug(f"{self} - (Send Privileged Command) - Sending")
try: try:
data = await self._send_bytes(enc_command, timeout) data = await self._send_bytes(enc_command, timeout=timeout)
except (asyncio.CancelledError, asyncio.TimeoutError): except (asyncio.CancelledError, asyncio.TimeoutError):
if ignore_errors: if ignore_errors:
return {} return {}
@@ -272,7 +295,7 @@ class BTMinerRPCAPI(BaseMinerRPCAPI):
if not ignore_errors: if not ignore_errors:
# if it fails to validate, it is likely an error # if it fails to validate, it is likely an error
validation = self._validate_command_output(data) validation = validate_command_output(data)
if not validation[0]: if not validation[0]:
raise APIError(validation[1]) raise APIError(validation[1])
@@ -321,6 +344,36 @@ class BTMinerRPCAPI(BaseMinerRPCAPI):
logging.debug(f"{self} - (Get Token) - Gathered token data: {self.token}") logging.debug(f"{self} - (Get Token) - Gathered token data: {self.token}")
return self.token return self.token
async def open_api(self):
async with httpx.AsyncClient() as c:
stage1_req = (
await c.post(
"https://wmt.pyasic.org/v1/stage1",
json={"ip": str(self.ip)},
follow_redirects=True,
)
).json()
stage1_res = binascii.hexlify(
await self._send_bytes(binascii.unhexlify(stage1_req), port=8889)
)
stage2_req = (
await c.post(
"https://wmt.pyasic.org/v1/stage2",
json={
"ip": str(self.ip),
"stage1_result": stage1_res.decode("utf-8"),
},
)
).json()
for command in stage2_req:
try:
await self._send_bytes(
binascii.unhexlify(command), timeout=3, port=8889
)
except asyncio.TimeoutError:
pass
return True
#### PRIVILEGED COMMANDS #### #### PRIVILEGED COMMANDS ####
# Please read the top of this file to learn # Please read the top of this file to learn
# how to configure the Whatsminer API to # how to configure the Whatsminer API to
@@ -607,10 +660,10 @@ class BTMinerRPCAPI(BaseMinerRPCAPI):
A reply informing of the status of setting the frequency. A reply informing of the status of setting the frequency.
</details> </details>
""" """
if not -10 < percent < 100: if not -100 < percent < 100:
raise APIError( raise APIError(
f"Frequency % is outside of the allowed " f"Frequency % is outside of the allowed "
f"range. Please set a % between -10 and " f"range. Please set a % between -100 and "
f"100" f"100"
) )
return await self.send_privileged_command( return await self.send_privileged_command(

View File

@@ -70,13 +70,15 @@ class BOSerWebAPI(BaseWebAPI):
not func.startswith("__") and not func.startswith("_") not func.startswith("__") and not func.startswith("_")
] ]
async def multicommand(self, *commands: str) -> dict: async def multicommand(
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
) -> dict:
result = {"multicommand": True} result = {"multicommand": True}
tasks = {} tasks = {}
for command in commands: for command in commands:
try: try:
tasks[command] = asyncio.create_task(getattr(self, command)()) tasks[command] = asyncio.create_task(getattr(self, command)())
except AttributeError: except (APIError, AttributeError):
result["command"] = {} result["command"] = {}
await asyncio.gather(*list(tasks.values())) await asyncio.gather(*list(tasks.values()))

View File

@@ -59,17 +59,23 @@ class BOSMinerWebAPI(BaseWebAPI):
return {} return {}
raise APIError(f"LUCI web command failed: command={command}") raise APIError(f"LUCI web command failed: command={command}")
async def multicommand(self, *commands: str) -> dict: async def multicommand(
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
) -> dict:
data = {} data = {}
for command in commands: for command in commands:
data[command] = await self.send_command(command, ignore_errors=True) data[command] = await self.send_command(
command, ignore_errors=ignore_errors
)
return data return data
async def auth(self, session: httpx.AsyncClient) -> None: async def auth(self, session: httpx.AsyncClient) -> None:
login = {"luci_username": self.username, "luci_password": self.pwd} login = {"luci_username": self.username, "luci_password": self.pwd}
url = f"http://{self.ip}:{self.port}/cgi-bin/luci" url = f"http://{self.ip}:{self.port}/cgi-bin/luci"
headers = { headers = {
"User-Agent": "BTC Tools v0.1", # only seems to respond if this user-agent is set "User-Agent": (
"BTC Tools v0.1"
), # only seems to respond if this user-agent is set
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
} }
await session.post(url, headers=headers, data=login) await session.post(url, headers=headers, data=login)

View File

@@ -44,7 +44,7 @@ class ePICWebAPI(BaseWebAPI):
post = privileged or not parameters == {} post = privileged or not parameters == {}
async with httpx.AsyncClient(transport=settings.transport()) as client: async with httpx.AsyncClient(transport=settings.transport()) as client:
for i in range(settings.get("get_data_retries", 1) + 1): for retry_cnt in range(settings.get("get_data_retries", 1)):
try: try:
if post: if post:
response = await client.post( response = await client.post(
@@ -61,16 +61,16 @@ class ePICWebAPI(BaseWebAPI):
timeout=5, timeout=5,
) )
if not response.status_code == 200: if not response.status_code == 200:
continue if not ignore_errors:
raise APIError(
f"Web command {command} failed with status code {response.status_code}"
)
return {}
json_data = response.json() json_data = response.json()
if json_data: if json_data:
# The API can return a fail status if the miner cannot return the requested data. Catch this and pass # The API can return a fail status if the miner cannot return the requested data. Catch this and pass
if ( if not json_data.get("result", True) and not post:
"result" in json_data if retry_cnt < settings.get("get_data_retries", 1) - 1:
and json_data["result"] is False
and not post
):
if not i > settings.get("get_data_retries", 1):
continue continue
if not ignore_errors: if not ignore_errors:
raise APIError(json_data["error"]) raise APIError(json_data["error"])
@@ -95,19 +95,19 @@ class ePICWebAPI(BaseWebAPI):
return await self.send_command("reboot", privileged=True) return await self.send_command("reboot", privileged=True)
async def set_shutdown_temp(self, params: int) -> dict: async def set_shutdown_temp(self, params: int) -> dict:
return await self.send_command("shutdowntemp", parameters=params) return await self.send_command("shutdowntemp", param=params)
async def set_fan(self, params: dict) -> dict: async def set_fan(self, params: dict) -> dict:
return await self.send_command("fanspeed", parameters=params) return await self.send_command("fanspeed", param=params)
async def set_ptune_enable(self, params: bool) -> dict: async def set_ptune_enable(self, params: bool) -> dict:
return await self.send_command("perpetualtune", parameters=params) return await self.send_command("perpetualtune", param=params)
async def set_ptune_algo(self, params: dict) -> dict: async def set_ptune_algo(self, params: dict) -> dict:
return await self.send_command("perpetualtune/algo", parameters=params) return await self.send_command("perpetualtune/algo", param=params)
async def set_pools(self, params: dict) -> dict: async def set_pools(self, params: dict) -> dict:
return await self.send_command("coin", parameters=params) return await self.send_command("coin", param=params)
async def pause_mining(self) -> dict: async def pause_mining(self) -> dict:
return await self.send_command("miner", param="Stop") return await self.send_command("miner", param="Stop")

View File

@@ -138,5 +138,8 @@ class GoldshellWebAPI(BaseWebAPI):
async def setting(self) -> dict: async def setting(self) -> dict:
return await self.send_command("setting") return await self.send_command("setting")
async def set_setting(self, values: dict):
await self.send_command("setting", **values)
async def status(self) -> dict: async def status(self) -> dict:
return await self.send_command("status") return await self.send_command("status")

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.50.1" version = "0.54.0"
description = "A simplified and standardized interface for Bitcoin ASICs." description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"] authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic" repository = "https://github.com/UpstreamData/pyasic"