Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36a273ec2b | ||
|
|
6a0dc03b9d | ||
|
|
ce7b006c8f | ||
|
|
88cc05bcea | ||
|
|
ae749f4a90 | ||
|
|
36b30a2cdd | ||
|
|
ae9f103578 | ||
|
|
13b583b739 |
@@ -9,6 +9,8 @@
|
||||
## Intro
|
||||
Welcome to pyasic! Pyasic uses an asynchronous method of communicating with asic miners on your network, which makes it super fast.
|
||||
|
||||
[Supported Miner Types](miners/supported_types.md)
|
||||
|
||||
Getting started with pyasic is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
|
||||
|
||||
<br>
|
||||
|
||||
59
docs/miners/antminer/X17.md
Normal file
59
docs/miners/antminer/X17.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# pyasic
|
||||
## X17 Models
|
||||
|
||||
## S17
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S17+
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.S17_Plus.BMMinerS17Plus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S17 Pro
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.S17_Pro.BMMinerS17Pro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S17e
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.S17e.BMMinerS17e
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## T17
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## T17+
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.T17_Plus.BMMinerT17Plus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
## T17e
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X17.T17e.BMMinerT17e
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
52
docs/miners/antminer/X19.md
Normal file
52
docs/miners/antminer/X19.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# pyasic
|
||||
## X19 Models
|
||||
|
||||
## S19
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19 Pro
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19_Pro.BMMinerS19Pro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
## S19a
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19a.BMMinerS19a
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
## S19j
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19j.BMMinerS19j
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19j Pro
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19j_Pro.BMMinerS19jPro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## T19
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
26
docs/miners/antminer/X9.md
Normal file
26
docs/miners/antminer/X9.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# pyasic
|
||||
## X9 Models
|
||||
|
||||
## S9
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S9i
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X9.S9i.BMMinerS9i
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## T9
|
||||
|
||||
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
26
docs/miners/avalonminer/A10X.md
Normal file
26
docs/miners/avalonminer/A10X.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# pyasic
|
||||
## A10X Models
|
||||
|
||||
## A1026
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## A1047
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## A1066
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
26
docs/miners/avalonminer/A7X.md
Normal file
26
docs/miners/avalonminer/A7X.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# pyasic
|
||||
## A7X Models
|
||||
|
||||
## A721
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## A741
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## A761
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
26
docs/miners/avalonminer/A8X.md
Normal file
26
docs/miners/avalonminer/A8X.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# pyasic
|
||||
## A8X Models
|
||||
|
||||
## A821
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## A841
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## A851
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
10
docs/miners/avalonminer/A9X.md
Normal file
10
docs/miners/avalonminer/A9X.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# pyasic
|
||||
## A9X Models
|
||||
|
||||
## A921
|
||||
|
||||
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
8
docs/miners/backends/bmminer.md
Normal file
8
docs/miners/backends/bmminer.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## BMMiner Backend
|
||||
|
||||
::: pyasic.miners._backends.bmminer.BMMiner
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
8
docs/miners/backends/bosminer.md
Normal file
8
docs/miners/backends/bosminer.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## BOSMiner Backend
|
||||
|
||||
::: pyasic.miners._backends.bosminer.BOSMiner
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
8
docs/miners/backends/btminer.md
Normal file
8
docs/miners/backends/btminer.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## BTMiner Backend
|
||||
|
||||
::: pyasic.miners._backends.btminer.BTMiner
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
8
docs/miners/backends/cgminer.md
Normal file
8
docs/miners/backends/cgminer.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## CGMiner Backend
|
||||
|
||||
::: pyasic.miners._backends.cgminer.CGMiner
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
8
docs/miners/backends/hiveon.md
Normal file
8
docs/miners/backends/hiveon.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## Hiveon Backend
|
||||
|
||||
::: pyasic.miners._backends.hiveon.Hiveon
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
73
docs/miners/supported_types.md
Normal file
73
docs/miners/supported_types.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# pyasic
|
||||
## Supported Miners
|
||||
|
||||
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
||||
|
||||
## Miner List
|
||||
|
||||
##### pyasic currently supports the following miners and subtypes:
|
||||
* Braiins OS+ Devices:
|
||||
* All devices supported by BraiinsOS+ are supported here.
|
||||
* Stock Firmware Whatsminers:
|
||||
* M3X Series:
|
||||
* [M30S][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30S]:
|
||||
* [VE10][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE10]
|
||||
* [VG20][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG20]
|
||||
* [VE20][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE20]
|
||||
* [V50][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV50]
|
||||
* [M30S+][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlus]:
|
||||
* [VF20][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVF20]
|
||||
* [VE40][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE40]
|
||||
* [VG60][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG60]
|
||||
* [M30S++][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlus]:
|
||||
* [VG30][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG30]
|
||||
* [VG40][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG40]
|
||||
* [M31S][pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31S]
|
||||
* [M31S+][pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlus]:
|
||||
* [VE20][pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE20]
|
||||
* [M32S][pyasic.miners.whatsminer.btminer.M3X.M32S.BTMinerM32S]
|
||||
* M2X Series:
|
||||
* [M20S][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S]:
|
||||
* [V10][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10]
|
||||
* [V20][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20]
|
||||
* [M20S+][pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlus]
|
||||
* [M21][pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21]
|
||||
* [M21S][pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21S]:
|
||||
* [V20][pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20]
|
||||
* [V60][pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60]
|
||||
* [M21S+][pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlus]
|
||||
* Stock Firmware Antminers:
|
||||
* X19 Series:
|
||||
* [S19][pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19]
|
||||
* [S19 Pro][pyasic.miners.antminer.bmminer.X19.S19_Pro.BMMinerS19Pro]
|
||||
* [S19a][pyasic.miners.antminer.bmminer.X19.S19a.BMMinerS19a]
|
||||
* [S19j][pyasic.miners.antminer.bmminer.X19.S19j.BMMinerS19j]
|
||||
* [S19j Pro][pyasic.miners.antminer.bmminer.X19.S19j_Pro.BMMinerS19jPro]
|
||||
* [T19][pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19]
|
||||
* X17 Series:
|
||||
* [S17][pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17]
|
||||
* [S17+][pyasic.miners.antminer.bmminer.X17.S17_Plus.BMMinerS17Plus]
|
||||
* [S17 Pro][pyasic.miners.antminer.bmminer.X17.S17_Pro.BMMinerS17Pro]
|
||||
* [S17e][pyasic.miners.antminer.bmminer.X17.S17e.BMMinerS17e]
|
||||
* [T17][pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17]
|
||||
* [T17+][pyasic.miners.antminer.bmminer.X17.T17_Plus.BMMinerT17Plus]
|
||||
* [T17e][pyasic.miners.antminer.bmminer.X17.T17e.BMMinerT17e]
|
||||
* X9 Series:
|
||||
* [S9][pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9]
|
||||
* [S9i][pyasic.miners.antminer.bmminer.X9.S9i.BMMinerS9i]
|
||||
* [T9][pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9]
|
||||
* Stock Firmware Avalonminers:
|
||||
* A7X Series:
|
||||
* [A721][pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721]
|
||||
* [A741][pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741]
|
||||
* [A761][pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761]
|
||||
* A8X Series:
|
||||
* [A821][pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821]
|
||||
* [A841][pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841]
|
||||
* [A851][pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851]
|
||||
* A9X Series:
|
||||
* [A921][pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921]
|
||||
* A10X Series:
|
||||
* [A1026][pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026]
|
||||
* [A1047][pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047]
|
||||
* [A1066][pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066]
|
||||
75
docs/miners/whatsminer/M2X.md
Normal file
75
docs/miners/whatsminer/M2X.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# pyasic
|
||||
## M2X Models
|
||||
|
||||
## M20S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20SV10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20SV20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20S+
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
## M21S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21S
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21SV20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21SV60
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21S+
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
131
docs/miners/whatsminer/M3X.md
Normal file
131
docs/miners/whatsminer/M3X.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# pyasic
|
||||
## M3X Models
|
||||
|
||||
## M30S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30S
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30SVE10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30SVG20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30SVE20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30SV50
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV50
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+VF20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVF20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+VE40
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+VG60
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG60
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S++
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S++VG30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+VG40
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
## M31S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31S
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31S+
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31S+VE20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M32S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M32S.BTMinerM32S
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
12
docs/network/miner_network_range.md
Normal file
12
docs/network/miner_network_range.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# pyasic
|
||||
## Miner Network Range
|
||||
|
||||
`MinerNetworkRange` is a class used by [`MinerNetwork`][pyasic.network.MinerNetwork] to handle any constructor stings.
|
||||
The goal is to emulate what is produced by `ipaddress.ip_network` by allowing [`MinerNetwork`][pyasic.network.MinerNetwork] to get a list of hosts.
|
||||
This allows this class to be the [`MinerNetwork.network`][pyasic.network.MinerNetwork] and hence be used for scanning.
|
||||
|
||||
::: pyasic.network.net_range.MinerNetworkRange
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
20
mkdocs.yml
20
mkdocs.yml
@@ -3,9 +3,29 @@ repo_url: https://github.com/UpstreamData/pyasic
|
||||
nav:
|
||||
- Introduction: "index.md"
|
||||
- Miners:
|
||||
- Supported Miners: "miners/supported_types.md"
|
||||
- Miner Factory: "miners/miner_factory.md"
|
||||
- Backends:
|
||||
- BMMiner: "miners/backends/bmminer.md"
|
||||
- BOSMiner: "miners/backends/bosminer.md"
|
||||
- BTMiner: "miners/backends/btminer.md"
|
||||
- CGMiner: "miners/backends/cgminer.md"
|
||||
- Hiveon: "miners/backends/hiveon.md"
|
||||
|
||||
- Classes:
|
||||
- Antminer X9: "miners/antminer/X9.md"
|
||||
- Antminer X17: "miners/antminer/X17.md"
|
||||
- Antminer X19: "miners/antminer/X19.md"
|
||||
- Avalon 7X: "miners/avalonminer/A7X.md"
|
||||
- Avalon 8X: "miners/avalonminer/A8X.md"
|
||||
- Avalon 9X: "miners/avalonminer/A9X.md"
|
||||
- Avalon 10X: "miners/avalonminer/A10X.md"
|
||||
- Whatsminer M2X: "miners/whatsminer/M2X.md"
|
||||
- Whatsminer M3X: "miners/whatsminer/M3X.md"
|
||||
|
||||
- Network:
|
||||
- Miner Network: "network/miner_network.md"
|
||||
- Miner Network Range: "network/miner_network_range.md"
|
||||
- Data:
|
||||
- Miner Data: "data/miner_data.md"
|
||||
- Config:
|
||||
|
||||
@@ -10,7 +10,7 @@ from passlib.handlers.md5_crypt import md5_crypt
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
from pyasic.API import BaseMinerAPI, APIError
|
||||
from pyasic.settings import WHATSMINER_PWD
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
### IMPORTANT ###
|
||||
@@ -161,7 +161,12 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
pwd: The admin password of the miner. Default is admin.
|
||||
"""
|
||||
|
||||
def __init__(self, ip: str, port: int = 4028, pwd: str = WHATSMINER_PWD):
|
||||
def __init__(
|
||||
self,
|
||||
ip: str,
|
||||
port: int = 4028,
|
||||
pwd: str = PyasicSettings().global_whatsminer_password,
|
||||
):
|
||||
super().__init__(ip, port)
|
||||
self.admin_pwd = pwd
|
||||
self.current_token = None
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import logging
|
||||
from pyasic.settings import DEBUG, LOGFILE
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
def init_logger():
|
||||
if LOGFILE:
|
||||
if PyasicSettings().logfile:
|
||||
logging.basicConfig(
|
||||
filename="logfile.txt",
|
||||
filemode="a",
|
||||
@@ -18,7 +18,7 @@ def init_logger():
|
||||
|
||||
_logger = logging.getLogger()
|
||||
|
||||
if DEBUG:
|
||||
if PyasicSettings().debug:
|
||||
_logger.setLevel(logging.DEBUG)
|
||||
logging.getLogger("asyncssh").setLevel(logging.DEBUG)
|
||||
else:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
|
||||
from pyasic.API.bmminer import BMMinerAPI
|
||||
@@ -7,10 +8,12 @@ from pyasic.miners import BaseMiner
|
||||
|
||||
from pyasic.data import MinerData
|
||||
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class BMMiner(BaseMiner):
|
||||
"""Base handler for BMMiner based miners."""
|
||||
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
@@ -19,10 +22,11 @@ class BMMiner(BaseMiner):
|
||||
self.uname = "root"
|
||||
self.pwd = "admin"
|
||||
|
||||
async def get_model(self) -> str or None:
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
:return: Miner model or None.
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
# check if model is cached
|
||||
if self.model:
|
||||
@@ -46,7 +50,8 @@ class BMMiner(BaseMiner):
|
||||
async def get_hostname(self) -> str:
|
||||
"""Get miner hostname.
|
||||
|
||||
:return: The hostname of the miner as a string or "?"
|
||||
Returns:
|
||||
The hostname of the miner as a string or "?"
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
@@ -72,12 +77,14 @@ class BMMiner(BaseMiner):
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
return "?"
|
||||
|
||||
async def send_ssh_command(self, cmd: str) -> str or None:
|
||||
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||
"""Send a command to the miner over ssh.
|
||||
|
||||
:param cmd: The command to run.
|
||||
Parameters:
|
||||
cmd: The command to run.
|
||||
|
||||
:return: Result of the command or None.
|
||||
Returns:
|
||||
Result of the command or None.
|
||||
"""
|
||||
result = None
|
||||
|
||||
@@ -101,10 +108,11 @@ class BMMiner(BaseMiner):
|
||||
# return the result, either command output or None
|
||||
return result
|
||||
|
||||
async def get_config(self) -> list or None:
|
||||
async def get_config(self) -> Union[list, None]:
|
||||
"""Get the pool configuration of the miner.
|
||||
|
||||
:return: Pool config data or None.
|
||||
Returns:
|
||||
Pool config data or None.
|
||||
"""
|
||||
# get pool data
|
||||
pools = await self.api.pools()
|
||||
@@ -120,6 +128,11 @@ class BMMiner(BaseMiner):
|
||||
return pool_data
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
"""Reboot the miner.
|
||||
|
||||
Returns:
|
||||
The result of rebooting the miner.
|
||||
"""
|
||||
logging.debug(f"{self}: Sending reboot command.")
|
||||
_ret = await self.send_ssh_command("reboot")
|
||||
logging.debug(f"{self}: Reboot command completed.")
|
||||
@@ -128,6 +141,11 @@ class BMMiner(BaseMiner):
|
||||
return False
|
||||
|
||||
async def get_data(self) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
||||
|
||||
board_offset = -1
|
||||
@@ -147,7 +165,7 @@ class BMMiner(BaseMiner):
|
||||
data.mac = mac
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary", "pools", "stats", ignore_x19_error=True
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ipaddress
|
||||
import logging
|
||||
import json
|
||||
from typing import Union
|
||||
|
||||
import toml
|
||||
|
||||
@@ -14,7 +15,7 @@ from pyasic.data import MinerData
|
||||
|
||||
from pyasic.config import MinerConfig
|
||||
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class BOSMiner(BaseMiner):
|
||||
@@ -27,10 +28,11 @@ class BOSMiner(BaseMiner):
|
||||
self.pwd = "admin"
|
||||
self.config = None
|
||||
|
||||
async def send_ssh_command(self, cmd: str) -> str or None:
|
||||
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||
"""Send a command to the miner over ssh.
|
||||
|
||||
:return: Result of the command or None.
|
||||
Returns:
|
||||
Result of the command or None.
|
||||
"""
|
||||
result = None
|
||||
|
||||
@@ -74,6 +76,7 @@ class BOSMiner(BaseMiner):
|
||||
return False
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
"""Restart bosminer hashing process. Wraps [`restart_bosminer`][pyasic.miners._backends.bosminer.BOSMiner.restart_bosminer] to standardize."""
|
||||
return await self.restart_bosminer()
|
||||
|
||||
async def restart_bosminer(self) -> bool:
|
||||
@@ -94,7 +97,12 @@ class BOSMiner(BaseMiner):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def get_config(self) -> None:
|
||||
async def get_config(self) -> MinerConfig:
|
||||
"""Gets the config for the miner and sets it as `self.config`.
|
||||
|
||||
Returns:
|
||||
The config from `self.config`.
|
||||
"""
|
||||
logging.debug(f"{self}: Getting config.")
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
logging.debug(f"{self}: Opening SFTP connection.")
|
||||
@@ -110,7 +118,8 @@ class BOSMiner(BaseMiner):
|
||||
async def get_hostname(self) -> str:
|
||||
"""Get miner hostname.
|
||||
|
||||
:return: The hostname of the miner as a string or "?"
|
||||
Returns:
|
||||
The hostname of the miner as a string or "?"
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
@@ -129,10 +138,11 @@ class BOSMiner(BaseMiner):
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
return "?"
|
||||
|
||||
async def get_model(self) -> str or None:
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
:return: Miner model or None.
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
# check if model is cached
|
||||
if self.model:
|
||||
@@ -166,10 +176,11 @@ class BOSMiner(BaseMiner):
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_version(self):
|
||||
async def get_version(self) -> Union[str, None]:
|
||||
"""Get miner firmware version.
|
||||
|
||||
:return: Miner firmware version or None.
|
||||
Returns:
|
||||
Miner firmware version or None.
|
||||
"""
|
||||
# check if version is cached
|
||||
if self.version:
|
||||
@@ -206,65 +217,21 @@ class BOSMiner(BaseMiner):
|
||||
.as_bos(model=self.model.replace(" (BOS)", ""))
|
||||
)
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
await conn.run("/etc/init.d/bosminer stop")
|
||||
logging.debug(f"{self}: Opening SFTP connection.")
|
||||
async with conn.start_sftp_client() as sftp:
|
||||
logging.debug(f"{self}: Opening config file.")
|
||||
async with sftp.open("/etc/bosminer.toml", "w+") as file:
|
||||
await file.write(toml_conf)
|
||||
logging.debug(f"{self}: Restarting BOSMiner")
|
||||
await conn.run("/etc/init.d/bosminer restart")
|
||||
|
||||
async def get_board_info(self) -> dict:
|
||||
"""Gets data on each board and chain in the miner."""
|
||||
logging.debug(f"{self}: Getting board info.")
|
||||
devdetails = await self.api.devdetails()
|
||||
if not devdetails.get("DEVDETAILS"):
|
||||
print("devdetails error", devdetails)
|
||||
return {0: [], 1: [], 2: []}
|
||||
devs = devdetails["DEVDETAILS"]
|
||||
boards = {}
|
||||
offset = devs[0]["ID"]
|
||||
for board in devs:
|
||||
boards[board["ID"] - offset] = []
|
||||
if not board["Chips"] == self.nominal_chips:
|
||||
nominal = False
|
||||
else:
|
||||
nominal = True
|
||||
boards[board["ID"] - offset].append(
|
||||
{
|
||||
"chain": board["ID"] - offset,
|
||||
"chip_count": board["Chips"],
|
||||
"chip_status": "o" * board["Chips"],
|
||||
"nominal": nominal,
|
||||
}
|
||||
)
|
||||
logging.debug(f"Found board data for {self}: {boards}")
|
||||
return boards
|
||||
|
||||
async def get_bad_boards(self) -> dict:
|
||||
"""Checks for and provides list of non working boards."""
|
||||
boards = await self.get_board_info()
|
||||
bad_boards = {}
|
||||
for board in boards.keys():
|
||||
for chain in boards[board]:
|
||||
if not chain["chip_count"] == 63:
|
||||
if board not in bad_boards.keys():
|
||||
bad_boards[board] = []
|
||||
bad_boards[board].append(chain)
|
||||
return bad_boards
|
||||
|
||||
async def check_good_boards(self) -> str:
|
||||
"""Checks for and provides list for working boards."""
|
||||
devs = await self.api.devdetails()
|
||||
bad = 0
|
||||
chains = devs["DEVDETAILS"]
|
||||
for chain in chains:
|
||||
if chain["Chips"] == 0:
|
||||
bad += 1
|
||||
if not bad > 0:
|
||||
return str(self.ip)
|
||||
await conn.run("/etc/init.d/bosminer start")
|
||||
|
||||
async def get_data(self) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
||||
|
||||
board_offset = -1
|
||||
@@ -284,7 +251,7 @@ class BOSMiner(BaseMiner):
|
||||
data.mac = mac
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
|
||||
from pyasic.API.btminer import BTMinerAPI
|
||||
@@ -9,7 +10,7 @@ from pyasic.API import APIError
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.data.error_codes import WhatsminerError
|
||||
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class BTMiner(BaseMiner):
|
||||
@@ -19,7 +20,12 @@ class BTMiner(BaseMiner):
|
||||
self.api = BTMinerAPI(ip)
|
||||
self.api_type = "BTMiner"
|
||||
|
||||
async def get_model(self):
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
if self.model:
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
@@ -31,7 +37,12 @@ class BTMiner(BaseMiner):
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
async def get_hostname(self) -> Union[str, None]:
|
||||
"""Get miner hostname.
|
||||
|
||||
Returns:
|
||||
The hostname of the miner as a string or None.
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
try:
|
||||
@@ -48,38 +59,12 @@ class BTMiner(BaseMiner):
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_board_info(self) -> dict:
|
||||
"""Gets data on each board and chain in the miner."""
|
||||
logging.debug(f"{self}: Getting board info.")
|
||||
devs = await self.api.devs()
|
||||
if not devs.get("DEVS"):
|
||||
print("devs error", devs)
|
||||
return {0: [], 1: [], 2: []}
|
||||
devs = devs["DEVS"]
|
||||
boards = {}
|
||||
offset = devs[0]["ID"]
|
||||
for board in devs:
|
||||
boards[board["ID"] - offset] = []
|
||||
if "Effective Chips" in board.keys():
|
||||
if not board["Effective Chips"] in self.nominal_chips:
|
||||
nominal = False
|
||||
else:
|
||||
nominal = True
|
||||
boards[board["ID"] - offset].append(
|
||||
{
|
||||
"chain": board["ID"] - offset,
|
||||
"chip_count": board["Effective Chips"],
|
||||
"chip_status": "o" * board["Effective Chips"],
|
||||
"nominal": nominal,
|
||||
}
|
||||
)
|
||||
else:
|
||||
logging.warning(f"Incorrect board data from {self}: {board}")
|
||||
print(board)
|
||||
logging.debug(f"Found board data for {self}: {boards}")
|
||||
return boards
|
||||
async def get_mac(self) -> str:
|
||||
"""Get the mac address of the miner.
|
||||
|
||||
async def get_mac(self):
|
||||
Returns:
|
||||
The mac address of the miner as a string.
|
||||
"""
|
||||
mac = ""
|
||||
data = await self.api.summary()
|
||||
if data:
|
||||
@@ -100,7 +85,12 @@ class BTMiner(BaseMiner):
|
||||
|
||||
return str(mac).upper()
|
||||
|
||||
async def get_data(self):
|
||||
async def get_data(self) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
||||
|
||||
mac = None
|
||||
@@ -126,7 +116,7 @@ class BTMiner(BaseMiner):
|
||||
data.hostname = hostname
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand("summary", "devs", "pools")
|
||||
if miner_data:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
|
||||
from pyasic.API.cgminer import CGMinerAPI
|
||||
@@ -8,7 +9,7 @@ from pyasic.API import APIError
|
||||
|
||||
from pyasic.data import MinerData
|
||||
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class CGMiner(BaseMiner):
|
||||
@@ -21,7 +22,12 @@ class CGMiner(BaseMiner):
|
||||
self.pwd = "admin"
|
||||
self.config = None
|
||||
|
||||
async def get_model(self):
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
if self.model:
|
||||
return self.model
|
||||
try:
|
||||
@@ -33,7 +39,12 @@ class CGMiner(BaseMiner):
|
||||
return self.model
|
||||
return None
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
async def get_hostname(self) -> Union[str, None]:
|
||||
"""Get miner hostname.
|
||||
|
||||
Returns:
|
||||
The hostname of the miner as a string or "?"
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
try:
|
||||
@@ -48,7 +59,15 @@ class CGMiner(BaseMiner):
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
async def send_ssh_command(self, cmd):
|
||||
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||
"""Send a command to the miner over ssh.
|
||||
|
||||
Parameters:
|
||||
cmd: The command to run.
|
||||
|
||||
Returns:
|
||||
Result of the command or None.
|
||||
"""
|
||||
result = None
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
for i in range(3):
|
||||
@@ -63,9 +82,11 @@ class CGMiner(BaseMiner):
|
||||
return result
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
"""Restart cgminer hashing process. Wraps [`restart_cgminer`][pyasic.miners._backends.cgminer.CGMiner.restart_cgminer] to standardize."""
|
||||
return await self.restart_cgminer()
|
||||
|
||||
async def restart_cgminer(self) -> bool:
|
||||
"""Restart cgminer hashing process."""
|
||||
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
|
||||
commands = ";".join(commands)
|
||||
_ret = await self.send_ssh_command(commands)
|
||||
@@ -74,6 +95,7 @@ class CGMiner(BaseMiner):
|
||||
return False
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
"""Reboots power to the physical miner."""
|
||||
logging.debug(f"{self}: Sending reboot command.")
|
||||
_ret = await self.send_ssh_command("reboot")
|
||||
logging.debug(f"{self}: Reboot command completed.")
|
||||
@@ -82,6 +104,7 @@ class CGMiner(BaseMiner):
|
||||
return False
|
||||
|
||||
async def start_cgminer(self) -> None:
|
||||
"""Start cgminer hashing process."""
|
||||
commands = [
|
||||
"mkdir -p /etc/tmp/",
|
||||
'echo "*/3 * * * * /usr/bin/cgminer-monitor" > /etc/tmp/root',
|
||||
@@ -92,6 +115,7 @@ class CGMiner(BaseMiner):
|
||||
await self.send_ssh_command(commands)
|
||||
|
||||
async def stop_cgminer(self) -> None:
|
||||
"""Restart cgminer hashing process."""
|
||||
commands = [
|
||||
"mkdir -p /etc/tmp/",
|
||||
'echo "" > /etc/tmp/root',
|
||||
@@ -101,14 +125,24 @@ class CGMiner(BaseMiner):
|
||||
commands = ";".join(commands)
|
||||
await self.send_ssh_command(commands)
|
||||
|
||||
async def get_config(self) -> None:
|
||||
async def get_config(self) -> str:
|
||||
"""Gets the config for the miner and sets it as `self.config`.
|
||||
|
||||
Returns:
|
||||
The config from `self.config`.
|
||||
"""
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
command = "cat /etc/config/cgminer"
|
||||
result = await conn.run(command, check=True)
|
||||
self.config = result.stdout
|
||||
print(str(self.config))
|
||||
return self.config
|
||||
|
||||
async def get_data(self):
|
||||
async def get_data(self) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
||||
|
||||
board_offset = -1
|
||||
@@ -128,7 +162,7 @@ class CGMiner(BaseMiner):
|
||||
data.mac = mac
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary", "pools", "stats", ignore_x19_error=True
|
||||
)
|
||||
|
||||
@@ -8,3 +8,21 @@ class M20S(BaseMiner):
|
||||
self.model = "M20S"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20SV10(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M20S"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20SV20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M20S"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .M20S import M20S
|
||||
from .M20S import M20S, M20SV10, M20SV20
|
||||
from .M20S_Plus import M20SPlus
|
||||
|
||||
from .M21 import M21
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon1026 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon1026(CGMiner, Avalon1026):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon1047 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon1047(CGMiner, Avalon1047):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon1066 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon1066(CGMiner, Avalon1066):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon721 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon721(CGMiner, Avalon721):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon741 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon741(CGMiner, Avalon741):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon761 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon761(CGMiner, Avalon761):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon821 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon821(CGMiner, Avalon821):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon841 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon841(CGMiner, Avalon841):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon851 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon851(CGMiner, Avalon851):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import Avalon921 # noqa - Ignore access to _module
|
||||
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
from pyasic.settings import PyasicSettings
|
||||
import re
|
||||
from pyasic.config import MinerConfig
|
||||
import logging
|
||||
@@ -67,7 +67,7 @@ class CGMinerAvalon921(CGMiner, Avalon921):
|
||||
data.model = model
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"version", "summary", "pools", "stats"
|
||||
)
|
||||
|
||||
@@ -23,10 +23,7 @@ import ipaddress
|
||||
import json
|
||||
import logging
|
||||
|
||||
from pyasic.settings import (
|
||||
MINER_FACTORY_GET_VERSION_RETRIES as GET_VERSION_RETRIES,
|
||||
NETWORK_PING_TIMEOUT as PING_TIMEOUT,
|
||||
)
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
import asyncssh
|
||||
|
||||
@@ -123,6 +120,8 @@ MINER_CLASSES = {
|
||||
"M20S": {
|
||||
"Default": BTMinerM20S,
|
||||
"BTMiner": BTMinerM20S,
|
||||
"10": BTMinerM20SV10,
|
||||
"20": BTMinerM20SV20,
|
||||
},
|
||||
"M20S+": {
|
||||
"Default": BTMinerM20SPlus,
|
||||
@@ -282,13 +281,12 @@ class MinerFactory(metaclass=Singleton):
|
||||
ver = None
|
||||
|
||||
# try to get the API multiple times based on retries
|
||||
for i in range(GET_VERSION_RETRIES):
|
||||
for i in range(PyasicSettings().miner_factory_get_version_retries):
|
||||
try:
|
||||
# get the API type, should be BOSMiner, CGMiner, BMMiner, BTMiner, or None
|
||||
new_model, new_api, new_ver = await asyncio.wait_for(
|
||||
self._get_miner_type(ip), timeout=PING_TIMEOUT
|
||||
self._get_miner_type(ip), timeout=10
|
||||
)
|
||||
|
||||
# keep track of the API and model we found first
|
||||
if new_api and not api:
|
||||
api = new_api
|
||||
@@ -296,13 +294,11 @@ class MinerFactory(metaclass=Singleton):
|
||||
model = new_model
|
||||
if new_ver and not ver:
|
||||
ver = new_ver
|
||||
|
||||
# if we find the API and model, don't need to loop anymore
|
||||
if api and model:
|
||||
break
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
logging.warning(f"{ip}: Get Miner Timed Out")
|
||||
# make sure we have model information
|
||||
if model:
|
||||
if not api:
|
||||
@@ -310,12 +306,10 @@ class MinerFactory(metaclass=Singleton):
|
||||
|
||||
if model not in MINER_CLASSES.keys():
|
||||
if "avalon" in model:
|
||||
print(model)
|
||||
if model == "avalon10":
|
||||
miner = CGMinerAvalon1066(str(ip))
|
||||
else:
|
||||
miner = CGMinerAvalon821(str(ip))
|
||||
miner = UnknownMiner(str(ip))
|
||||
return miner
|
||||
if api not in MINER_CLASSES[model].keys():
|
||||
api = "Default"
|
||||
@@ -355,32 +349,26 @@ class MinerFactory(metaclass=Singleton):
|
||||
async def _get_miner_type(
|
||||
self, ip: ipaddress.ip_address or str
|
||||
) -> Tuple[str or None, str or None, str or None]:
|
||||
data = None
|
||||
|
||||
model = None
|
||||
api = None
|
||||
ver = None
|
||||
|
||||
devdetails = None
|
||||
version = None
|
||||
|
||||
try:
|
||||
# get device details and version data
|
||||
data = await self._send_api_command(str(ip), "devdetails+version")
|
||||
|
||||
# validate success
|
||||
validation = await self._validate_command(data)
|
||||
if not validation[0]:
|
||||
raise APIError(validation[1])
|
||||
|
||||
# copy each part of the main command to devdetails and version
|
||||
devdetails = data["devdetails"][0]
|
||||
version = data["version"][0]
|
||||
|
||||
except APIError:
|
||||
# if getting data fails we need to check again
|
||||
data = None
|
||||
|
||||
# if data is None then get it a slightly different way
|
||||
if not data:
|
||||
try:
|
||||
# try devdetails and version separately (X19s mainly require this)
|
||||
# get devdetails and validate
|
||||
@@ -405,6 +393,32 @@ class MinerFactory(metaclass=Singleton):
|
||||
# catch APIError and let the factory know we cant get data
|
||||
logging.warning(f"{ip}: API Command Error: {e}")
|
||||
return None, None, None
|
||||
except OSError as e:
|
||||
# miner refused connection on API port, we wont be able to get data this way
|
||||
# try ssh
|
||||
try:
|
||||
async with asyncssh.connect(
|
||||
str(ip),
|
||||
known_hosts=None,
|
||||
username="root",
|
||||
password="admin",
|
||||
server_host_key_algs=["ssh-rsa"],
|
||||
) as conn:
|
||||
board_name = None
|
||||
cmd = await conn.run("cat /tmp/sysinfo/board_name")
|
||||
if cmd:
|
||||
board_name = cmd.stdout.strip()
|
||||
|
||||
if board_name:
|
||||
if board_name == "am1-s9":
|
||||
model = "Antminer S9"
|
||||
if board_name == "am2-s17":
|
||||
model = "Antminer S17"
|
||||
api = "BOSMiner+"
|
||||
return model, api, None
|
||||
|
||||
except asyncssh.misc.PermissionDenied:
|
||||
return None, None, None
|
||||
|
||||
# if we have devdetails, we can get model data from there
|
||||
if devdetails:
|
||||
@@ -452,24 +466,24 @@ class MinerFactory(metaclass=Singleton):
|
||||
if "BOSminer+" in version["VERSION"][0].keys():
|
||||
api = "BOSMiner+"
|
||||
|
||||
# check for avalonminers
|
||||
if version["VERSION"][0].get("PROD"):
|
||||
_data = version["VERSION"][0]["PROD"].split("-")
|
||||
model = _data[0]
|
||||
if len(data) > 1:
|
||||
ver = _data[1]
|
||||
elif version["VERSION"][0].get("MODEL"):
|
||||
_data = version["VERSION"][0]["MODEL"].split("-")
|
||||
model = f"AvalonMiner {_data[0]}"
|
||||
if len(data) > 1:
|
||||
ver = _data[1]
|
||||
|
||||
# if all that fails, check the Description to see if it is a whatsminer
|
||||
if version.get("Description") and (
|
||||
"whatsminer" in version.get("Description")
|
||||
):
|
||||
api = "BTMiner"
|
||||
|
||||
# check for avalonminers
|
||||
if version["VERSION"][0].get("PROD"):
|
||||
_data = version["VERSION"][0]["PROD"].split("-")
|
||||
model = _data[0]
|
||||
if len(data) > 1:
|
||||
ver = _data[1]
|
||||
elif version["VERSION"][0].get("MODEL"):
|
||||
_data = version["VERSION"][0]["MODEL"].split("-")
|
||||
model = f"AvalonMiner {_data[0]}"
|
||||
if len(data) > 1:
|
||||
ver = _data[1]
|
||||
|
||||
# if we have no model from devdetails but have version, try to get it from there
|
||||
if version and not model:
|
||||
# make sure version isn't blank
|
||||
@@ -486,23 +500,6 @@ class MinerFactory(metaclass=Singleton):
|
||||
elif "am2-s17" in version["STATUS"][0]["Description"]:
|
||||
model = "Antminer S17"
|
||||
|
||||
# final try on a braiins OS bug with devdetails not returning
|
||||
else:
|
||||
try:
|
||||
async with asyncssh.connect(
|
||||
str(ip),
|
||||
known_hosts=None,
|
||||
username="root",
|
||||
password="admin",
|
||||
server_host_key_algs=["ssh-rsa"],
|
||||
) as conn:
|
||||
cfg = await conn.run("bosminer config --data")
|
||||
if cfg:
|
||||
cfg = json.loads(cfg.stdout)
|
||||
model = cfg.get("data").get("format").get("model")
|
||||
except asyncssh.misc.PermissionDenied:
|
||||
pass
|
||||
|
||||
if model:
|
||||
# whatsminer have a V in their version string (M20SV41), remove everything after it
|
||||
if "V" in model:
|
||||
@@ -519,7 +516,7 @@ class MinerFactory(metaclass=Singleton):
|
||||
async def _validate_command(data: dict) -> Tuple[bool, str or None]:
|
||||
"""Check if the returned command output is correctly formatted."""
|
||||
# check if the data returned is correct or an error
|
||||
if not data:
|
||||
if not data or data == {}:
|
||||
return False, "No API data."
|
||||
# if status isn't a key, it is a multicommand
|
||||
if "STATUS" not in data.keys():
|
||||
@@ -548,9 +545,10 @@ class MinerFactory(metaclass=Singleton):
|
||||
# get reader and writer streams
|
||||
reader, writer = await asyncio.open_connection(str(ip), 4028)
|
||||
except OSError as e:
|
||||
if e.errno in [10061, 22] or e.winerror == 1225:
|
||||
raise e
|
||||
logging.warning(f"{str(ip)} - Command {command}: {e}")
|
||||
return {}
|
||||
|
||||
# create the command
|
||||
cmd = {"command": command}
|
||||
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
from pyasic.miners._backends import BTMiner # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import M20S # noqa - Ignore access to _module
|
||||
from pyasic.miners._types import ( # noqa - Ignore access to _module
|
||||
M20S,
|
||||
M20SV10,
|
||||
M20SV20,
|
||||
)
|
||||
|
||||
|
||||
class BTMinerM20S(BTMiner, M20S):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM20SV10(BTMiner, M20SV10):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM20SV20(BTMiner, M20SV20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .M20S import BTMinerM20S
|
||||
from .M20S import BTMinerM20S, BTMinerM20SV10, BTMinerM20SV20
|
||||
from .M20S_Plus import BTMinerM20SPlus
|
||||
|
||||
from .M21 import BTMinerM21
|
||||
|
||||
@@ -14,7 +14,7 @@ class BTMinerM30S(BTMiner, M30S):
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SV50(BTMiner, M30SV50):
|
||||
class BTMinerM30SVE10(BTMiner, M30SVE10):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
@@ -32,7 +32,7 @@ class BTMinerM30SVE20(BTMiner, M30SVE20):
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SVE10(BTMiner, M30SVE10):
|
||||
class BTMinerM30SV50(BTMiner, M30SV50):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -13,13 +13,13 @@ class BTMinerM30SPlus(BTMiner, M30SPlus):
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusVE40(BTMiner, M30SPlusVE40):
|
||||
class BTMinerM30SPlusVF20(BTMiner, M30SPlusVF20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusVF20(BTMiner, M30SPlusVF20):
|
||||
class BTMinerM30SPlusVE40(BTMiner, M30SPlusVE40):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,13 +12,13 @@ class BTMinerM30SPlusPlus(BTMiner, M30SPlusPlus):
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusPlusVG40(BTMiner, M30SPlusPlusVG40):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusPlusVG30(BTMiner, M30SPlusPlusVG30):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusPlusVG40(BTMiner, M30SPlusPlusVG40):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -4,12 +4,8 @@ import logging
|
||||
from typing import Union
|
||||
|
||||
from pyasic.network.net_range import MinerNetworkRange
|
||||
from pyasic.miners.miner_factory import MinerFactory
|
||||
from pyasic.settings import (
|
||||
NETWORK_PING_RETRIES as PING_RETRIES,
|
||||
NETWORK_PING_TIMEOUT as PING_TIMEOUT,
|
||||
NETWORK_SCAN_THREADS as SCAN_THREADS,
|
||||
)
|
||||
from pyasic.miners.miner_factory import MinerFactory, AnyMiner
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class MinerNetwork:
|
||||
@@ -91,7 +87,7 @@ class MinerNetwork:
|
||||
for host in local_network.hosts():
|
||||
|
||||
# make sure we don't exceed the allowed async tasks
|
||||
if len(scan_tasks) < SCAN_THREADS:
|
||||
if len(scan_tasks) < PyasicSettings().network_scan_threads:
|
||||
# add the task to the list
|
||||
scan_tasks.append(self.ping_and_get_miner(host))
|
||||
else:
|
||||
@@ -130,7 +126,7 @@ class MinerNetwork:
|
||||
# for each ip on the network, loop through and scan it
|
||||
for host in local_network.hosts():
|
||||
# make sure we don't exceed the allowed async tasks
|
||||
if len(scan_tasks) >= SCAN_THREADS:
|
||||
if len(scan_tasks) >= PyasicSettings().network_scan_threads:
|
||||
# scanned is a loopable list of awaitables
|
||||
scanned = asyncio.as_completed(scan_tasks)
|
||||
# when we scan, empty the scan tasks
|
||||
@@ -150,24 +146,42 @@ class MinerNetwork:
|
||||
|
||||
@staticmethod
|
||||
async def ping_miner(ip: ipaddress.ip_address) -> None or ipaddress.ip_address:
|
||||
return await ping_miner(ip)
|
||||
miner = await ping_miner(ip)
|
||||
if miner:
|
||||
return miner
|
||||
miner = await ping_miner(ip, port=4029)
|
||||
if miner:
|
||||
return miner
|
||||
miner = await ping_miner(ip, port=8889)
|
||||
if miner:
|
||||
return miner
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def ping_and_get_miner(
|
||||
ip: ipaddress.ip_address,
|
||||
) -> None or ipaddress.ip_address:
|
||||
return await ping_and_get_miner(ip)
|
||||
) -> None or AnyMiner:
|
||||
miner = await ping_and_get_miner(ip)
|
||||
if miner:
|
||||
return miner
|
||||
miner = await ping_and_get_miner(ip, port=4029)
|
||||
if miner:
|
||||
return miner
|
||||
miner = await ping_and_get_miner(ip, port=8889)
|
||||
if miner:
|
||||
return miner
|
||||
return None
|
||||
|
||||
|
||||
async def ping_miner(
|
||||
ip: ipaddress.ip_address, port=4028
|
||||
) -> None or ipaddress.ip_address:
|
||||
for i in range(PING_RETRIES):
|
||||
for i in range(PyasicSettings().network_ping_retries):
|
||||
connection_fut = asyncio.open_connection(str(ip), port)
|
||||
try:
|
||||
# get the read and write streams from the connection
|
||||
reader, writer = await asyncio.wait_for(
|
||||
connection_fut, timeout=PING_TIMEOUT
|
||||
connection_fut, timeout=PyasicSettings().network_ping_timeout
|
||||
)
|
||||
# immediately close connection, we know connection happened
|
||||
writer.close()
|
||||
@@ -188,15 +202,13 @@ async def ping_miner(
|
||||
return
|
||||
|
||||
|
||||
async def ping_and_get_miner(
|
||||
ip: ipaddress.ip_address, port=4028
|
||||
) -> None or ipaddress.ip_address:
|
||||
for i in range(PING_RETRIES):
|
||||
async def ping_and_get_miner(ip: ipaddress.ip_address, port=4028) -> None or AnyMiner:
|
||||
for i in range(PyasicSettings().network_ping_retries):
|
||||
connection_fut = asyncio.open_connection(str(ip), port)
|
||||
try:
|
||||
# get the read and write streams from the connection
|
||||
reader, writer = await asyncio.wait_for(
|
||||
connection_fut, timeout=PING_TIMEOUT
|
||||
connection_fut, timeout=PyasicSettings().network_ping_timeout
|
||||
)
|
||||
# immediately close connection, we know connection happened
|
||||
writer.close()
|
||||
|
||||
@@ -8,9 +8,9 @@ class MinerNetworkRange:
|
||||
Parameters:
|
||||
ip_range: ## A range of IP addresses to put in the network, or a list of IPs
|
||||
* Takes a string formatted as:
|
||||
* {ip_range_1_start}-{ip_range_1_end}, {ip_address_1}, {ip_range_2_start}-{ip_range_2_end}, {ip_address_2}...
|
||||
* Also takes a list of strings formatted as:
|
||||
* [{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]
|
||||
```f"{ip_range_1_start}-{ip_range_1_end}, {ip_address_1}, {ip_range_2_start}-{ip_range_2_end}, {ip_address_2}..."```
|
||||
* Also takes a list of strings or `ipaddress.ipaddress` formatted as:
|
||||
```[{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]```
|
||||
"""
|
||||
|
||||
def __init__(self, ip_range: Union[str, list]):
|
||||
|
||||
@@ -1,53 +1,26 @@
|
||||
import toml
|
||||
import os
|
||||
|
||||
NETWORK_PING_RETRIES: int = 3
|
||||
NETWORK_PING_TIMEOUT: int = 5
|
||||
NETWORK_SCAN_THREADS: int = 300
|
||||
|
||||
CFG_UTIL_REBOOT_THREADS: int = 300
|
||||
CFG_UTIL_CONFIG_THREADS: int = 300
|
||||
|
||||
MINER_FACTORY_GET_VERSION_RETRIES: int = 3
|
||||
|
||||
WHATSMINER_PWD = "admin"
|
||||
|
||||
DEBUG = False
|
||||
LOGFILE = False
|
||||
|
||||
settings_keys = {}
|
||||
|
||||
try:
|
||||
with open(
|
||||
os.path.join(os.path.dirname(__file__), "settings.toml"), "r"
|
||||
) as settings_file:
|
||||
settings = toml.loads(settings_file.read())
|
||||
settings_keys = settings.keys()
|
||||
except:
|
||||
pass
|
||||
|
||||
if "ping_retries" in settings_keys:
|
||||
NETWORK_PING_RETRIES: int = settings["ping_retries"]
|
||||
if "ping_timeout" in settings_keys:
|
||||
NETWORK_PING_TIMEOUT: int = settings["ping_timeout"]
|
||||
if "scan_threads" in settings_keys:
|
||||
NETWORK_SCAN_THREADS: int = settings["scan_threads"]
|
||||
|
||||
if "reboot_threads" in settings_keys:
|
||||
CFG_UTIL_REBOOT_THREADS: int = settings["reboot_threads"]
|
||||
if "config_threads" in settings_keys:
|
||||
CFG_UTIL_CONFIG_THREADS: int = settings["config_threads"]
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
if "get_version_retries" in settings_keys:
|
||||
MINER_FACTORY_GET_VERSION_RETRIES: int = settings["get_version_retries"]
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
if "whatsminer_pwd" in settings_keys:
|
||||
WHATSMINER_PWD: str = settings["whatsminer_pwd"]
|
||||
@dataclass
|
||||
class PyasicSettings(metaclass=Singleton):
|
||||
network_ping_retries: int = 3
|
||||
network_ping_timeout: int = 5
|
||||
network_scan_threads: int = 300
|
||||
|
||||
if "debug" in settings_keys:
|
||||
DEBUG: int = settings["debug"]
|
||||
miner_factory_get_version_retries: int = 1
|
||||
|
||||
if "logfile" in settings_keys:
|
||||
LOGFILE: bool = settings["logfile"]
|
||||
miner_get_data_retries: int = 1
|
||||
|
||||
global_whatsminer_password = "admin"
|
||||
|
||||
debug: bool = False
|
||||
logfile: bool = False
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
get_version_retries = 3
|
||||
ping_retries = 3
|
||||
ping_timeout = 3 # Seconds
|
||||
scan_threads = 300
|
||||
config_threads = 300
|
||||
reboot_threads = 300
|
||||
|
||||
|
||||
### IMPORTANT ###
|
||||
# You need to change the password of the miners using the whatsminer
|
||||
# tool or the privileged API will not work using admin as the password.
|
||||
# If you change the password, you can pass that password here.
|
||||
|
||||
whatsminer_pwd = "admin"
|
||||
|
||||
logfile = true
|
||||
|
||||
### DEBUG MODE ###
|
||||
# change this to debug = true
|
||||
# to enable debug mode.
|
||||
debug = false
|
||||
# debug = true
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "pyasic"
|
||||
version = "0.11.1"
|
||||
version = "0.12.2"
|
||||
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
|
||||
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||
repository = "https://github.com/UpstreamData/pyasic"
|
||||
|
||||
Reference in New Issue
Block a user