Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87016670d4 | ||
|
|
8701bbe4e2 | ||
|
|
7d1f125b0b | ||
|
|
e433902bd5 | ||
|
|
a653772968 | ||
|
|
d8b938cd5b | ||
|
|
47d76e325a | ||
|
|
7ee7868094 | ||
|
|
3f1183a4f9 | ||
|
|
2b443497ea | ||
|
|
c3972f9524 | ||
|
|
92bbb21c11 | ||
|
|
1980ff2563 | ||
|
|
93d09a1483 | ||
|
|
690d0d99df | ||
|
|
78f689eb2c | ||
|
|
e68f188e8f | ||
|
|
7eda611fe9 | ||
|
|
1d12817942 | ||
|
|
b24efd4c69 | ||
|
|
5533135b05 | ||
|
|
475054fbe0 | ||
|
|
06bad1bbe0 | ||
|
|
f3746ff756 | ||
|
|
9f16d37c8b | ||
|
|
8a13c7940a | ||
|
|
8bea76ff67 | ||
|
|
1504bd744c | ||
|
|
6449f10615 | ||
|
|
d79509bda7 | ||
|
|
630b847466 | ||
|
|
ed11611919 | ||
|
|
e2431c938d | ||
|
|
60f4b4a5ed | ||
|
|
d41097af20 | ||
|
|
8a5d505731 | ||
|
|
36e76c6f41 | ||
|
|
717b9421dd | ||
|
|
d2f71e8c94 | ||
|
|
697991f28f | ||
|
|
b0e18ab766 | ||
|
|
e39a6921d0 | ||
|
|
aac1be0565 | ||
|
|
683fcb2138 | ||
|
|
9fbbef9b18 | ||
|
|
6e0b9a0a7b | ||
|
|
7f472f6f4f | ||
|
|
b7d7b33ab9 | ||
|
|
da11c0bb1f | ||
|
|
eae433d2bd | ||
|
|
c16bc37aff | ||
|
|
255b06ac9e | ||
|
|
29ec619126 | ||
|
|
247def04ff | ||
|
|
4600e7d953 | ||
|
|
850c266555 | ||
|
|
ad374fe2fb | ||
|
|
5ca39b6fe7 | ||
|
|
b50dd26e6f | ||
|
|
53eaccaa9b | ||
|
|
91f207316a | ||
|
|
1e37418909 | ||
|
|
4c09ba5529 | ||
|
|
7bab4747ad | ||
|
|
fd8cc7378c | ||
|
|
8aeef4d5e7 | ||
|
|
4bafde9da7 | ||
|
|
5a3107aecf | ||
|
|
7e758720f0 | ||
|
|
39e3e249f8 | ||
|
|
118c5b056e | ||
|
|
2c3b5599fe | ||
|
|
e421eaa324 | ||
|
|
75d6bc6808 | ||
|
|
98c547e416 | ||
|
|
45250e36e4 | ||
|
|
fa7544d052 | ||
|
|
53f3fc5ee9 | ||
|
|
1b36de4131 | ||
|
|
6f0c6f6284 | ||
|
|
b7dda5bf87 | ||
|
|
14f33a40c3 | ||
|
|
5c904aced0 | ||
|
|
53a3bbf531 | ||
|
|
50586f1ce7 | ||
|
|
9f6235a0fc | ||
|
|
4d21f150ce | ||
|
|
7c0dfc49dd | ||
|
|
269b13f6c1 | ||
|
|
a9bb7d2e5a | ||
|
|
11295f27a7 | ||
|
|
55aa3dd85b | ||
|
|
20272d4360 | ||
|
|
623dc92ef2 | ||
|
|
2d59394b1e | ||
|
|
26c2095ff1 | ||
|
|
ec7d241caa | ||
|
|
d0432ed1aa | ||
|
|
8c5503d002 | ||
|
|
6d6f950c95 | ||
|
|
30745e54ba | ||
|
|
c3fd94e79e | ||
|
|
2924a8d67b | ||
|
|
9f4c4bb9cf | ||
|
|
3d6eebf06e | ||
|
|
b3d9b6ff7e | ||
|
|
60facacc48 | ||
|
|
b8a6063838 | ||
|
|
bcba2be524 | ||
|
|
f7187d2017 | ||
|
|
d91b7c4406 | ||
|
|
248a7e6d69 |
@@ -1,5 +1,5 @@
|
||||
# pyasic
|
||||
*A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH.*
|
||||
*A simplified and standardized interface for Bitcoin ASICs.*
|
||||
|
||||
[](https://github.com/psf/black)
|
||||
[](https://pypi.org/project/pyasic/)
|
||||
|
||||
@@ -31,6 +31,8 @@ def backend_str(backend: MinerTypes) -> str:
|
||||
return "Stock Firmware Avalonminers"
|
||||
case MinerTypes.VNISH:
|
||||
return "Vnish Firmware Miners"
|
||||
case MinerTypes.EPIC:
|
||||
return "ePIC Firmware Miners"
|
||||
case MinerTypes.BRAIINS_OS:
|
||||
return "BOS+ Firmware Miners"
|
||||
case MinerTypes.HIVEON:
|
||||
|
||||
@@ -19,7 +19,7 @@ Getting started with pyasic is easy. First, find your miner (or miners) on the
|
||||
|
||||
## Scanning for miners
|
||||
To scan for miners in pyasic, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
|
||||
The command [`MinerNetwork().scan_network_for_miners()`][pyasic.network.MinerNetwork.scan_network_for_miners] returns a list that contains any miners found.
|
||||
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
|
||||
```python
|
||||
import asyncio # asyncio for handling the async part
|
||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
||||
@@ -28,7 +28,7 @@ from pyasic.network import MinerNetwork # miner network handles the scanning
|
||||
async def scan_miners(): # define async scan function to allow awaiting
|
||||
# create a miner network
|
||||
# you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
||||
network = MinerNetwork("192.168.1.50") # this uses the 192.168.1.0-255 network
|
||||
network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
|
||||
|
||||
# scan for miners asynchronously
|
||||
# this will return the correct type of miners if they are supported with all functionality.
|
||||
@@ -263,7 +263,7 @@ Pyasic implements a few dataclasses as helpers to make data return types consist
|
||||
|
||||
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns.
|
||||
|
||||
You can call [`MinerData.asdict()`][pyasic.data.MinerData.asdict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
||||
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
||||
|
||||
[`MinerData`][pyasic.data.MinerData] instances can also be added to each other to combine their data and can be divided by a number to divide all their data, allowing you to get average data from many miners by doing -
|
||||
```python
|
||||
@@ -288,3 +288,28 @@ It is the return from [`get_config()`](#get-config).
|
||||
|
||||
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
||||
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
||||
|
||||
## Settings
|
||||
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the [`pyasic.settings`][pyasic.settings] module, used as follows:
|
||||
|
||||
```python
|
||||
from pyasic import settings
|
||||
|
||||
settings.update("default_antminer_password", "my_pwd")
|
||||
```
|
||||
|
||||
Here are of all the settings, and their default values:
|
||||
```
|
||||
"network_ping_retries": 1,
|
||||
"network_ping_timeout": 3,
|
||||
"network_scan_threads": 300,
|
||||
"factory_get_retries": 1,
|
||||
"get_data_retries": 1,
|
||||
"default_whatsminer_password": "admin",
|
||||
"default_innosilicon_password": "admin",
|
||||
"default_antminer_password": "root",
|
||||
"default_bosminer_password": "root",
|
||||
"default_vnish_password": "admin",
|
||||
"default_epic_password": "letmein",
|
||||
"default_goldshell_password": "123456789",
|
||||
```
|
||||
|
||||
@@ -29,6 +29,20 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19i
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19+
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19j No PIC
|
||||
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
|
||||
handler: python
|
||||
@@ -113,6 +127,13 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19j Pro (BOS)
|
||||
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## T19 (BOS)
|
||||
::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19
|
||||
handler: python
|
||||
@@ -176,3 +197,38 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19 (ePIC)
|
||||
::: pyasic.miners.antminer.epic.X19.S19.ePICS19
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19 Pro (ePIC)
|
||||
::: pyasic.miners.antminer.epic.X19.S19.ePICS19Pro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19j (ePIC)
|
||||
::: pyasic.miners.antminer.epic.X19.S19.ePICS19j
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19j Pro (ePIC)
|
||||
::: pyasic.miners.antminer.epic.X19.S19.ePICS19jPro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## S19 XP (ePIC)
|
||||
::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
8
docs/miners/backends/epic.md
Normal file
8
docs/miners/backends/epic.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## ePIC Backend
|
||||
|
||||
::: pyasic.miners.backends.epic.ePIC
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
@@ -1,6 +1,14 @@
|
||||
# pyasic
|
||||
## Miner Factory
|
||||
|
||||
[`MinerFactory`][pyasic.miners.miner_factory.MinerFactory] is the way to create miner types in `pyasic`. The most important method is [`get_miner()`][pyasic.get_miner], which is mapped to [`pyasic.get_miner()`][pyasic.get_miner], and should be used from there.
|
||||
|
||||
The instance used for [`pyasic.get_miner()`][pyasic.get_miner] is `pyasic.miner_factory`.
|
||||
|
||||
[`MinerFactory`][pyasic.MinerFactory] also keeps a cache, which can be cleared if needed with `pyasic.miner_factory.clear_cached_miners()`.
|
||||
|
||||
Finally, there is functionality to get multiple miners without using `asyncio.gather()` explicitly. Use `pyasic.miner_factory.get_multiple_miners()` with a list of IPs as strings to get a list of miner instances. You can also get multiple miners with an `AsyncGenerator` by using `pyasic.miner_factory.get_miner_generator()`.
|
||||
|
||||
::: pyasic.miners.miner_factory.MinerFactory
|
||||
handler: python
|
||||
options:
|
||||
|
||||
@@ -10,9 +10,6 @@ details {
|
||||
padding-top:0px;
|
||||
padding-bottom:0px;
|
||||
}
|
||||
ul {
|
||||
margin:0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<details>
|
||||
@@ -73,6 +70,8 @@ ul {
|
||||
<li><a href="../antminer/X19#s19l">S19L</a></li>
|
||||
<li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
|
||||
<li><a href="../antminer/X19#s19j">S19j</a></li>
|
||||
<li><a href="../antminer/X19#s19i">S19i</a></li>
|
||||
<li><a href="../antminer/X19#s19_1">S19+</a></li>
|
||||
<li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
|
||||
<li><a href="../antminer/X19#s19-pro_1">S19 Pro+</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
|
||||
@@ -94,6 +93,8 @@ ul {
|
||||
<li><a href="../whatsminer/M2X#m20s-v10">M20S V10</a></li>
|
||||
<li><a href="../whatsminer/M2X#m20s-v20">M20S V20</a></li>
|
||||
<li><a href="../whatsminer/M2X#m20s-v30">M20S V30</a></li>
|
||||
<li><a href="../whatsminer/M2X#m20p-v10">M20P V10</a></li>
|
||||
<li><a href="../whatsminer/M2X#m20p-v30">M20P V30</a></li>
|
||||
<li><a href="../whatsminer/M2X#m20s_1-v30">M20S+ V30</a></li>
|
||||
<li><a href="../whatsminer/M2X#m21-v10">M21 V10</a></li>
|
||||
<li><a href="../whatsminer/M2X#m21s-v20">M21S V20</a></li>
|
||||
@@ -108,6 +109,8 @@ ul {
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X#m30-v10">M30 V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30-v20">M30 V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30k-v10">M30K V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s-v10">M30S V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s-v20">M30S V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s-v30">M30S V30</a></li>
|
||||
@@ -157,6 +160,7 @@ ul {
|
||||
<li><a href="../whatsminer/M3X#m30s_1-ve100">M30S+ VE100</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s_1-vf20">M30S+ VF20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s_1-vf30">M30S+ VF30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s_1-vg20">M30S+ VG20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s_1-vg30">M30S+ VG30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s_1-vg40">M30S+ VG40</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30s_1-vg50">M30S+ VG50</a></li>
|
||||
@@ -190,6 +194,9 @@ ul {
|
||||
<li><a href="../whatsminer/M3X#m30s_1_1-vj30">M30S++ VJ30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31-v10">M31 V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31-v20">M31 V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31h-v10">M31H V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31h-v40">M31H V40</a></li>
|
||||
<li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31s-v10">M31S V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31s-v20">M31S V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31s-v30">M31S V30</a></li>
|
||||
@@ -205,7 +212,6 @@ ul {
|
||||
<li><a href="../whatsminer/M3X#m31se-v10">M31SE V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31se-v20">M31SE V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31se-v30">M31SE V30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31h-v40">M31H V40</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31s_1-v10">M31S+ V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31s_1-v20">M31S+ V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m31s_1-v30">M31S+ V30</a></li>
|
||||
@@ -232,6 +238,7 @@ ul {
|
||||
<li><a href="../whatsminer/M3X#m33-v20">M33 V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m33-v30">M33 V30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m33s-vg30">M33S VG30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m33s_1-vg20">M33S+ VG20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m33s_1-vh20">M33S+ VH20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m33s_1-vh30">M33S+ VH30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m33s_1_1-vh20">M33S++ VH20</a></li>
|
||||
@@ -241,12 +248,15 @@ ul {
|
||||
<li><a href="../whatsminer/M3X#m36s-ve10">M36S VE10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m36s_1-vg30">M36S+ VG30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m36s_1_1-vh30">M36S++ VH30</a></li>
|
||||
<li><a href="../whatsminer/M3X#m39-v10">M39 V10</a></li>
|
||||
<li><a href="../whatsminer/M3X#m39-v20">M39 V20</a></li>
|
||||
<li><a href="../whatsminer/M3X#m39-v30">M39 V30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>M5X Series:</summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M5X#m50-ve30">M50 VE30</a></li>
|
||||
<li><a href="../whatsminer/M5X#m50-vg30">M50 VG30</a></li>
|
||||
<li><a href="../whatsminer/M5X#m50-vh10">M50 VH10</a></li>
|
||||
<li><a href="../whatsminer/M5X#m50-vh20">M50 VH20</a></li>
|
||||
@@ -397,6 +407,7 @@ ul {
|
||||
<li><a href="../antminer/X19#s19j-bos">S19j (BOS)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-no-pic-bos">S19j No PIC (BOS)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro-bos">S19j Pro (BOS)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro-bos">S19j Pro (BOS)</a></li>
|
||||
<li><a href="../antminer/X19#t19-bos">T19 (BOS)</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
@@ -434,6 +445,21 @@ ul {
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>ePIC Firmware Miners:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary>X19 Series:</summary>
|
||||
<ul>
|
||||
<li><a href="../antminer/X19#s19-epic">S19 (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-pro-epic">S19j Pro (ePIC)</a></li>
|
||||
<li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>HiveOS Firmware Miners:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
@@ -454,4 +480,4 @@ ul {
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
</details>
|
||||
@@ -29,6 +29,20 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20P V10
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20P V30
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20S+ V30
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
|
||||
handler: python
|
||||
|
||||
@@ -15,6 +15,20 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30K V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30K.BTMinerM30KV10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30L V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30L.BTMinerM30LV10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV10
|
||||
handler: python
|
||||
@@ -358,6 +372,13 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+ VG20
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30S+ VG30
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG30
|
||||
handler: python
|
||||
@@ -589,6 +610,27 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31H V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31H V40
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M30L V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31L.BTMinerM31LV10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31S V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV10
|
||||
handler: python
|
||||
@@ -694,13 +736,6 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31H V40
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M31S+ V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV10
|
||||
handler: python
|
||||
@@ -883,6 +918,13 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M33S+ VG20
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVG20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M33S+ VH20
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVH20
|
||||
handler: python
|
||||
@@ -946,6 +988,13 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M39 V10
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M39 V20
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V20
|
||||
handler: python
|
||||
@@ -953,3 +1002,10 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M39 V30
|
||||
::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# pyasic
|
||||
## M5X Models
|
||||
|
||||
## M50 VE30
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50 VG30
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30
|
||||
handler: python
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# pyasic
|
||||
## Miner Network Range
|
||||
|
||||
[`MinerNetworkRange`][pyasic.network.net_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
|
||||
35
docs/settings/settings.md
Normal file
35
docs/settings/settings.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# pyasic
|
||||
## settings
|
||||
|
||||
All settings here are global settings for all of pyasic. Set these settings with `update(key, value)`.
|
||||
|
||||
Settings options:
|
||||
- `network_ping_retries`
|
||||
- `network_ping_timeout`
|
||||
- `network_scan_threads`
|
||||
- `factory_get_retries`
|
||||
- `factory_get_timeout`
|
||||
- `get_data_retries`
|
||||
- `api_function_timeout`
|
||||
- `default_whatsminer_password`
|
||||
- `default_innosilicon_password`
|
||||
- `default_antminer_password`
|
||||
- `default_bosminer_password`
|
||||
- `default_vnish_password`
|
||||
- `default_goldshell_password`
|
||||
- `socket_linger_time`
|
||||
|
||||
|
||||
### get
|
||||
::: pyasic.settings.get
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
### update
|
||||
::: pyasic.settings.update
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
@@ -7,7 +7,6 @@ nav:
|
||||
- Miner Factory: "miners/miner_factory.md"
|
||||
- Network:
|
||||
- Miner Network: "network/miner_network.md"
|
||||
- Miner Network Range: "network/miner_network_range.md"
|
||||
- Dataclasses:
|
||||
- Miner Data: "data/miner_data.md"
|
||||
- Error Codes: "data/error_codes.md"
|
||||
@@ -30,6 +29,7 @@ nav:
|
||||
- CGMiner: "miners/backends/cgminer.md"
|
||||
- LUXMiner: "miners/backends/luxminer.md"
|
||||
- VNish: "miners/backends/vnish.md"
|
||||
- ePIC: "miners/backends/epic.md"
|
||||
- Hiveon: "miners/backends/hiveon.md"
|
||||
- Classes:
|
||||
- Antminer X3: "miners/antminer/X3.md"
|
||||
@@ -53,7 +53,8 @@ nav:
|
||||
- Goldshell X5: "miners/goldshell/X5.md"
|
||||
- Goldshell XMax: "miners/goldshell/XMax.md"
|
||||
- Base Miner: "miners/base_miner.md"
|
||||
|
||||
- Settings:
|
||||
- Settings: "settings/settings.md"
|
||||
|
||||
plugins:
|
||||
- mkdocstrings
|
||||
|
||||
@@ -20,7 +20,7 @@ import json
|
||||
import logging
|
||||
import re
|
||||
import warnings
|
||||
from typing import Tuple, Union
|
||||
from typing import Union
|
||||
|
||||
from pyasic.errors import APIError, APIWarning
|
||||
|
||||
@@ -83,15 +83,15 @@ class BaseMinerAPI:
|
||||
data = self._load_api_data(data)
|
||||
|
||||
# check for if the user wants to allow errors to return
|
||||
if not ignore_errors:
|
||||
# validate the command succeeded
|
||||
validation = self._validate_command_output(data)
|
||||
if not validation[0]:
|
||||
if allow_warning:
|
||||
logging.warning(
|
||||
f"{self.ip}: API Command Error: {command}: {validation[1]}"
|
||||
)
|
||||
validation = self._validate_command_output(data)
|
||||
if not validation[0]:
|
||||
if not ignore_errors:
|
||||
# validate the command succeeded
|
||||
raise APIError(validation[1])
|
||||
if allow_warning:
|
||||
logging.warning(
|
||||
f"{self.ip}: API Command Error: {command}: {validation[1]}"
|
||||
)
|
||||
|
||||
logging.debug(f"{self} - (Send Command) - Received data.")
|
||||
return data
|
||||
@@ -118,11 +118,12 @@ class BaseMinerAPI:
|
||||
data = await self.send_command(command, allow_warning=allow_warning)
|
||||
except APIError as e:
|
||||
# try to identify the error
|
||||
if ":" in e.message:
|
||||
err_command = e.message.split(":")[0]
|
||||
if err_command in commands:
|
||||
commands.remove(err_command)
|
||||
continue
|
||||
if e.message is not None:
|
||||
if ":" in e.message:
|
||||
err_command = e.message.split(":")[0]
|
||||
if err_command in commands:
|
||||
commands.remove(err_command)
|
||||
continue
|
||||
return {command: [{}] for command in commands}
|
||||
logging.debug(f"{self} - (Multicommand) - Received data")
|
||||
data["multicommand"] = True
|
||||
@@ -206,16 +207,18 @@ If you are sure you want to use this command please use API.send_command("{comma
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
|
||||
await writer.drain()
|
||||
try:
|
||||
ret_data = await asyncio.wait_for(reader.read(4096), timeout=timeout)
|
||||
except ConnectionAbortedError:
|
||||
return b"{}"
|
||||
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.
|
||||
ret_data += await asyncio.wait_for(reader.read(1), timeout=1)
|
||||
except asyncio.TimeoutError:
|
||||
return ret_data
|
||||
# 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
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving")
|
||||
@@ -254,13 +257,22 @@ If you are sure you want to use this command please use API.send_command("{comma
|
||||
# this is an error
|
||||
return False, f"{key}: " + data[key][0]["STATUS"][0]["Msg"]
|
||||
elif "id" not in data.keys():
|
||||
if isinstance(data["STATUS"], list):
|
||||
if data["STATUS"][0].get("STATUS", None) in ["S", "I"]:
|
||||
return True, None
|
||||
else:
|
||||
return False, data["STATUS"][0]["Msg"]
|
||||
|
||||
if data["STATUS"] not in ["S", "I"]:
|
||||
return False, data["Msg"]
|
||||
else:
|
||||
# make sure the command succeeded
|
||||
if type(data["STATUS"]) == str:
|
||||
if isinstance(data["STATUS"], str):
|
||||
if data["STATUS"] in ["RESTART"]:
|
||||
return True, None
|
||||
elif isinstance(data["STATUS"], dict):
|
||||
if data["STATUS"].get("STATUS") in ["S", "I"]:
|
||||
return True, None
|
||||
elif data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
||||
# this is an error
|
||||
if data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
||||
|
||||
@@ -22,15 +22,15 @@ import hashlib
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from typing import Union
|
||||
from typing import Literal, Union
|
||||
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from passlib.handlers.md5_crypt import md5_crypt
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.API import BaseMinerAPI
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.misc import api_min_version
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
### IMPORTANT ###
|
||||
# you need to change the password of the miners using the Whatsminer
|
||||
@@ -40,6 +40,12 @@ from pyasic.settings import PyasicSettings
|
||||
# you change the password, you can pass that to this class as pwd,
|
||||
# or add it as the Whatsminer_pwd in the settings.toml file.
|
||||
|
||||
PrePowerOnMessage = Union[
|
||||
Literal["wait for adjust temp"],
|
||||
Literal["adjust complete"],
|
||||
Literal["adjust continue"],
|
||||
]
|
||||
|
||||
|
||||
def _crypt(word: str, salt: str) -> str:
|
||||
"""Encrypts a word with a salt, using a standard salt format.
|
||||
@@ -186,7 +192,7 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
ip: str,
|
||||
api_ver: str = "0.0.0",
|
||||
port: int = 4028,
|
||||
pwd: str = PyasicSettings().global_whatsminer_password,
|
||||
pwd: str = settings.get("default_whatsminer_password", "admin"),
|
||||
):
|
||||
super().__init__(ip, port)
|
||||
self.pwd = pwd
|
||||
@@ -693,7 +699,7 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
)
|
||||
return await self.send_privileged_command("set_power_pct", percent=str(percent))
|
||||
|
||||
async def pre_power_on(self, complete: bool, msg: str) -> dict:
|
||||
async def pre_power_on(self, complete: bool, msg: PrePowerOnMessage) -> dict:
|
||||
"""Configure or check status of pre power on.
|
||||
|
||||
<details>
|
||||
@@ -713,7 +719,7 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
</details>
|
||||
"""
|
||||
|
||||
if not msg == "wait for adjust temp" or "adjust complete" or "adjust continue":
|
||||
if msg not in ("wait for adjust temp", "adjust complete", "adjust continue"):
|
||||
raise APIError(
|
||||
"Message is incorrect, please choose one of "
|
||||
'["wait for adjust temp", '
|
||||
@@ -729,6 +735,34 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
)
|
||||
|
||||
### ADDED IN V2.0.5 Whatsminer API ###
|
||||
|
||||
@api_min_version("2.0.5")
|
||||
async def set_power_pct_v2(self, percent: int) -> dict:
|
||||
"""Set the power percentage of the miner based on current power. Used for temporary adjustment. Added in API v2.0.5.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Set the power percentage of the miner, only works after changing
|
||||
the password of the miner using the Whatsminer tool.
|
||||
|
||||
Parameters:
|
||||
percent: The power percentage to set.
|
||||
Returns:
|
||||
A reply informing of the status of setting the power percentage.
|
||||
</details>
|
||||
"""
|
||||
|
||||
if not 0 < percent < 100:
|
||||
raise APIError(
|
||||
f"Power PCT % is outside of the allowed "
|
||||
f"range. Please set a % between 0 and "
|
||||
f"100"
|
||||
)
|
||||
return await self.send_privileged_command(
|
||||
"set_power_pct_v2", percent=str(percent)
|
||||
)
|
||||
|
||||
@api_min_version("2.0.5")
|
||||
async def set_temp_offset(self, temp_offset: int) -> dict:
|
||||
"""Set the offset of miner hash board target temperature.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
from pyasic import settings
|
||||
from pyasic.API.bmminer import BMMinerAPI
|
||||
from pyasic.API.bosminer import BOSMinerAPI
|
||||
from pyasic.API.btminer import BTMinerAPI
|
||||
@@ -29,10 +30,9 @@ from pyasic.data import (
|
||||
from pyasic.errors import APIError, APIWarning
|
||||
from pyasic.miners import get_miner
|
||||
from pyasic.miners.base import AnyMiner
|
||||
from pyasic.miners.miner_factory import MinerFactory
|
||||
from pyasic.miners.miner_factory import MinerFactory, miner_factory
|
||||
from pyasic.miners.miner_listener import MinerListener
|
||||
from pyasic.network import MinerNetwork
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
__all__ = [
|
||||
"BMMinerAPI",
|
||||
@@ -51,7 +51,8 @@ __all__ = [
|
||||
"get_miner",
|
||||
"AnyMiner",
|
||||
"MinerFactory",
|
||||
"miner_factory",
|
||||
"MinerListener",
|
||||
"MinerNetwork",
|
||||
"PyasicSettings",
|
||||
"settings",
|
||||
]
|
||||
|
||||
@@ -241,7 +241,7 @@ class MinerData:
|
||||
if item.hashrate is not None:
|
||||
hr_data.append(item.hashrate)
|
||||
if len(hr_data) > 0:
|
||||
return sum(hr_data)
|
||||
return round(sum(hr_data), 2)
|
||||
return self._hashrate
|
||||
|
||||
@hashrate.setter
|
||||
@@ -338,13 +338,16 @@ class MinerData:
|
||||
pass
|
||||
|
||||
def asdict(self) -> dict:
|
||||
logging.debug(f"MinerData - (To Dict) - Dumping Dict data")
|
||||
return asdict(self)
|
||||
|
||||
def as_dict(self) -> dict:
|
||||
"""Get this dataclass as a dictionary.
|
||||
|
||||
Returns:
|
||||
A dictionary version of this class.
|
||||
"""
|
||||
logging.debug(f"MinerData - (To Dict) - Dumping Dict data")
|
||||
return asdict(self)
|
||||
return self.asdict()
|
||||
|
||||
def as_json(self) -> str:
|
||||
"""Get this dataclass as JSON.
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
from dataclasses import asdict, dataclass, field, fields
|
||||
|
||||
C_N_CODES = ["52", "53", "54", "55", "56"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class WhatsminerError:
|
||||
@@ -37,10 +35,8 @@ class WhatsminerError:
|
||||
|
||||
@property
|
||||
def error_message(self): # noqa - Skip PyCharm inspection
|
||||
if len(str(self.error_code)) > 3 and str(self.error_code)[:2] in C_N_CODES:
|
||||
# 55 error code base has chip numbers, so the format is
|
||||
# 55 -> board num len 1 -> chip num len 3
|
||||
err_type = 55
|
||||
if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1":
|
||||
err_type = int(str(self.error_code)[:2])
|
||||
err_subtype = int(str(self.error_code)[2:3])
|
||||
err_value = int(str(self.error_code)[3:])
|
||||
else:
|
||||
@@ -88,7 +84,9 @@ class WhatsminerError:
|
||||
|
||||
ERROR_CODES = {
|
||||
1: { # Fan error
|
||||
0: {0: "Fan unknown."},
|
||||
0: {
|
||||
0: "Fan unknown.",
|
||||
},
|
||||
1: { # Fan speed error of 1000+
|
||||
0: "Intake fan speed error.",
|
||||
1: "Exhaust fan speed error.",
|
||||
@@ -101,7 +99,9 @@ ERROR_CODES = {
|
||||
0: "Intake fan speed error. Fan speed deviates by more than 3000.",
|
||||
1: "Exhaust fan speed error. Fan speed deviates by more than 3000.",
|
||||
},
|
||||
4: {0: "Fan speed too high."}, # High speed
|
||||
4: {
|
||||
0: "Fan speed too high.",
|
||||
}, # High speed
|
||||
},
|
||||
2: { # Power error
|
||||
0: {
|
||||
@@ -126,6 +126,7 @@ ERROR_CODES = {
|
||||
6: "Power remained unchanged for a long time.",
|
||||
7: "Power set enable error.",
|
||||
8: "Power input voltage is lower than 230V for high power mode.",
|
||||
9: "Power input current is incorrect.",
|
||||
},
|
||||
3: {
|
||||
3: "Power output high temperature protection error.",
|
||||
@@ -159,6 +160,8 @@ ERROR_CODES = {
|
||||
6: {
|
||||
3: "Power communication warning.",
|
||||
4: "Power communication error.",
|
||||
5: "Power unknown error.",
|
||||
6: "Power unknown error.",
|
||||
7: "Power watchdog protection.",
|
||||
8: "Power output high current protection.",
|
||||
9: "Power input high current protection.",
|
||||
@@ -170,57 +173,134 @@ ERROR_CODES = {
|
||||
3: "Power input too high warning.",
|
||||
4: "Power fan warning.",
|
||||
5: "Power high temperature warning.",
|
||||
6: "Power unknown error.",
|
||||
7: "Power unknown error.",
|
||||
8: "Power unknown error.",
|
||||
9: "Power unknown error.",
|
||||
},
|
||||
8: {
|
||||
0: "Power unknown error.",
|
||||
1: "Power vendor status 1 bit 0 error.",
|
||||
2: "Power vendor status 1 bit 1 error.",
|
||||
3: "Power vendor status 1 bit 2 error.",
|
||||
4: "Power vendor status 1 bit 3 error.",
|
||||
5: "Power vendor status 1 bit 4 error.",
|
||||
6: "Power vendor status 1 bit 5 error.",
|
||||
7: "Power vendor status 1 bit 6 error.",
|
||||
8: "Power vendor status 1 bit 7 error.",
|
||||
9: "Power vendor status 2 bit 0 error.",
|
||||
},
|
||||
9: {
|
||||
0: "Power vendor status 2 bit 1 error.",
|
||||
1: "Power vendor status 2 bit 2 error.",
|
||||
2: "Power vendor status 2 bit 3 error.",
|
||||
3: "Power vendor status 2 bit 4 error.",
|
||||
4: "Power vendor status 2 bit 5 error.",
|
||||
5: "Power vendor status 2 bit 6 error.",
|
||||
6: "Power vendor status 2 bit 7 error.",
|
||||
},
|
||||
},
|
||||
3: { # temperature error
|
||||
0: { # sensor detection error
|
||||
"n": "Slot {n} temperature sensor detection error."
|
||||
"n": "Slot {n} temperature sensor detection error.",
|
||||
},
|
||||
2: { # temperature reading error
|
||||
"n": "Slot {n} temperature reading error.",
|
||||
9: "Control board temperature sensor communication error.",
|
||||
},
|
||||
5: {"n": "Slot {n} temperature protecting."}, # temperature protection
|
||||
6: {0: "Hashboard high temperature error."}, # high temp
|
||||
5: {
|
||||
"n": "Slot {n} temperature protecting.",
|
||||
}, # temperature protection
|
||||
6: {
|
||||
0: "Hashboard high temperature error.",
|
||||
1: "Hashboard high temperature error.",
|
||||
2: "Hashboard high temperature error.",
|
||||
3: "Hashboard high temperature error.",
|
||||
}, # high temp
|
||||
7: {
|
||||
0: "The environment temperature fluctuates too much.",
|
||||
}, # env temp
|
||||
8: {
|
||||
0: "Humidity sensor not found.",
|
||||
1: "Humidity sensor read error.",
|
||||
2: "Humidity sensor read error.",
|
||||
3: "Humidity sensor protecting.",
|
||||
},
|
||||
}, # humidity
|
||||
},
|
||||
4: { # EEPROM error
|
||||
0: {0: "Eeprom unknown error."},
|
||||
1: {"n": "Slot {n} eeprom detection error."}, # EEPROM detection error
|
||||
2: {"n": "Slot {n} eeprom parsing error."}, # EEPROM parsing error
|
||||
3: {"n": "Slot {n} chip bin type error."}, # chip bin error
|
||||
4: {"n": "Slot {n} eeprom chip number X error."}, # EEPROM chip number error
|
||||
5: {"n": "Slot {n} eeprom xfer error."}, # EEPROM xfer error
|
||||
0: {
|
||||
0: "Eeprom unknown error.",
|
||||
},
|
||||
1: {
|
||||
"n": "Slot {n} eeprom detection error.",
|
||||
}, # EEPROM detection error
|
||||
2: {
|
||||
"n": "Slot {n} eeprom parsing error.",
|
||||
}, # EEPROM parsing error
|
||||
3: {
|
||||
"n": "Slot {n} chip bin type error.",
|
||||
}, # chip bin error
|
||||
4: {
|
||||
"n": "Slot {n} eeprom chip number X error.",
|
||||
}, # EEPROM chip number error
|
||||
5: {
|
||||
"n": "Slot {n} eeprom xfer error.",
|
||||
}, # EEPROM xfer error
|
||||
},
|
||||
5: { # hashboard error
|
||||
0: {0: "Board unknown error."},
|
||||
1: {"n": "Slot {n} miner type error."}, # board miner type error
|
||||
2: {"n": "Slot {n} bin type error."}, # chip bin type error
|
||||
3: {"n": "Slot {n} not found."}, # board not found error
|
||||
4: {"n": "Slot {n} error reading chip id."}, # reading chip id error
|
||||
5: {"n": "Slot {n} has bad chips."}, # board has bad chips error
|
||||
6: {"n": "Slot {n} loss of balance error."}, # loss of balance error
|
||||
7: {"n": "Slot {n} xfer error chip."}, # xfer error
|
||||
8: {"n": "Slot {n} reset error."}, # reset error
|
||||
9: {"n": "Slot {n} frequency too low."}, # freq error
|
||||
0: {
|
||||
0: "Board unknown error.",
|
||||
},
|
||||
1: {
|
||||
"n": "Slot {n} miner type error.",
|
||||
}, # board miner type error
|
||||
2: {
|
||||
"n": "Slot {n} bin type error.",
|
||||
}, # chip bin type error
|
||||
3: {
|
||||
"n": "Slot {n} not found.",
|
||||
}, # board not found error
|
||||
4: {
|
||||
"n": "Slot {n} error reading chip id.",
|
||||
}, # reading chip id error
|
||||
5: {
|
||||
"n": "Slot {n} has bad chips.",
|
||||
}, # board has bad chips error
|
||||
6: {
|
||||
"n": "Slot {n} loss of balance error.",
|
||||
}, # loss of balance error
|
||||
7: {
|
||||
"n": "Slot {n} xfer error chip.",
|
||||
}, # xfer error
|
||||
8: {
|
||||
"n": "Slot {n} reset error.",
|
||||
}, # reset error
|
||||
9: {
|
||||
"n": "Slot {n} frequency too low.",
|
||||
}, # freq error
|
||||
},
|
||||
6: { # env temp error
|
||||
0: {0: "Environment temperature is too high."}, # normal env temp error
|
||||
0: {
|
||||
0: "Environment temperature is too high.",
|
||||
}, # normal env temp error
|
||||
1: { # high power env temp error
|
||||
0: "Environment temperature is too high for high performance mode."
|
||||
0: "Environment temperature is too high for high performance mode.",
|
||||
},
|
||||
},
|
||||
7: { # control board error
|
||||
0: {0: "MAC address invalid", 1: "Control board no support chip."},
|
||||
0: {
|
||||
0: "MAC address invalid",
|
||||
1: "Control board no support chip.",
|
||||
},
|
||||
1: {
|
||||
0: "Control board rebooted as an exception.",
|
||||
1: "Control board rebooted as exception and cpufreq reduced, please upgrade the firmware",
|
||||
2: "Control board rebooted as an exception.",
|
||||
3: "The network is unstable, change time.",
|
||||
4: "Unknown error.",
|
||||
},
|
||||
2: {
|
||||
"n": "Control board slot {n} frame error.",
|
||||
},
|
||||
},
|
||||
8: { # checksum error
|
||||
@@ -228,63 +308,152 @@ ERROR_CODES = {
|
||||
0: "CGMiner checksum error.",
|
||||
1: "System monitor checksum error.",
|
||||
2: "Remote daemon checksum error.",
|
||||
}
|
||||
},
|
||||
1: {0: "Air to liquid PCB serial # does not match."},
|
||||
},
|
||||
9: {0: {1: "Power rate error."}}, # power rate error
|
||||
9: {
|
||||
0: {0: "Unknown error.", 1: "Power rate error.", 2: "Unknown error."}
|
||||
}, # power rate error
|
||||
20: { # pool error
|
||||
1: {0: "All pools are disabled."}, # all disabled error
|
||||
2: {"n": "Pool {n} connection failed."}, # pool connection failed error
|
||||
3: {0: "High rejection rate on pool."}, # rejection rate error
|
||||
0: {
|
||||
0: "No pool information configured.",
|
||||
},
|
||||
1: {
|
||||
0: "All pools are disabled.",
|
||||
}, # all disabled error
|
||||
2: {
|
||||
"n": "Pool {n} connection failed.",
|
||||
}, # pool connection failed error
|
||||
3: {
|
||||
0: "High rejection rate on pool.",
|
||||
}, # rejection rate error
|
||||
4: { # asicboost not supported error
|
||||
0: "The pool does not support asicboost mode."
|
||||
0: "The pool does not support asicboost mode.",
|
||||
},
|
||||
},
|
||||
21: {1: {"n": "Slot {n} factory test step failed."}},
|
||||
21: {
|
||||
1: {
|
||||
"n": "Slot {n} factory test step failed.",
|
||||
}
|
||||
},
|
||||
23: { # hashrate error
|
||||
1: {0: "Hashrate is too low."},
|
||||
2: {0: "Hashrate is too low."},
|
||||
3: {0: "Hashrate loss is too high."},
|
||||
4: {0: "Hashrate loss is too high."},
|
||||
5: {0: "Hashrate loss."},
|
||||
1: {
|
||||
0: "Hashrate is too low.",
|
||||
},
|
||||
2: {
|
||||
0: "Hashrate is too low.",
|
||||
},
|
||||
3: {
|
||||
0: "Hashrate loss is too high.",
|
||||
},
|
||||
4: {
|
||||
0: "Hashrate loss is too high.",
|
||||
},
|
||||
5: {
|
||||
0: "Hashrate loss.",
|
||||
},
|
||||
},
|
||||
50: { # water velocity error/voltage error
|
||||
1: {"n": "Slot {n} chip voltage too low."},
|
||||
2: {"n": "Slot {n} chip voltage changed."},
|
||||
3: {"n": "Slot {n} chip temperature difference is too large."},
|
||||
4: {"n": "Slot {n} chip hottest temperature difference is too large."},
|
||||
7: {"n": "Slot {n} water velocity is abnormal."}, # abnormal water velocity
|
||||
8: {0: "Chip temp calibration failed, please restore factory settings."},
|
||||
9: {"n": "Slot {n} chip temp calibration check no balance."},
|
||||
1: {
|
||||
"n": "Slot {n} chip voltage too low.",
|
||||
},
|
||||
2: {
|
||||
"n": "Slot {n} chip voltage changed.",
|
||||
},
|
||||
3: {
|
||||
"n": "Slot {n} chip temperature difference is too large.",
|
||||
},
|
||||
4: {
|
||||
"n": "Slot {n} chip hottest temperature difference is too large.",
|
||||
},
|
||||
5: {"n": "Slot {n} stopped hashing, chips temperature protecting."},
|
||||
7: {
|
||||
"n": "Slot {n} water velocity is abnormal.",
|
||||
}, # abnormal water velocity
|
||||
8: {
|
||||
0: "Chip temp calibration failed, please restore factory settings.",
|
||||
},
|
||||
9: {
|
||||
"n": "Slot {n} chip temp calibration check no balance.",
|
||||
},
|
||||
},
|
||||
51: { # frequency error
|
||||
1: {"n": "Slot {n} frequency up timeout."}, # frequency up timeout
|
||||
7: {"n": "Slot {n} frequency up timeout."}, # frequency up timeout
|
||||
1: {
|
||||
"n": "Slot {n} frequency up timeout.",
|
||||
}, # frequency up timeout
|
||||
2: {"n": "Slot {n} too many CRC errors."},
|
||||
3: {"n": "Slot {n} unstable."},
|
||||
7: {
|
||||
"n": "Slot {n} frequency up timeout.",
|
||||
}, # frequency up timeout
|
||||
},
|
||||
52: {
|
||||
"n": {
|
||||
"c": "Slot {n} chip {c} error nonce.",
|
||||
},
|
||||
},
|
||||
53: {
|
||||
"n": {
|
||||
"c": "Slot {n} chip {c} too few nonce.",
|
||||
},
|
||||
},
|
||||
54: {
|
||||
"n": {
|
||||
"c": "Slot {n} chip {c} temp protected.",
|
||||
},
|
||||
},
|
||||
55: {
|
||||
"n": {
|
||||
"c": "Slot {n} chip {c} has been reset.",
|
||||
},
|
||||
},
|
||||
56: {
|
||||
"n": {
|
||||
"c": "Slot {n} chip {c} zero nonce.",
|
||||
},
|
||||
},
|
||||
52: {"n": {"c": "Slot {n} chip {c} error nonce."}},
|
||||
53: {"n": {"c": "Slot {n} chip {c} too few nonce."}},
|
||||
54: {"n": {"c": "Slot {n} chip {c} temp protected."}},
|
||||
55: {"n": {"c": "Slot {n} chip {c} has been reset."}},
|
||||
56: {"n": {"c": "Slot {n} chip {c} does not return to the nonce."}},
|
||||
80: {
|
||||
0: {0: "The tool version is too low, please update."},
|
||||
1: {0: "Low freq."},
|
||||
2: {0: "Low hashrate."},
|
||||
3: {5: "High env temp."},
|
||||
0: {
|
||||
0: "The tool version is too low, please update.",
|
||||
},
|
||||
1: {
|
||||
0: "Low freq.",
|
||||
},
|
||||
2: {
|
||||
0: "Low hashrate.",
|
||||
},
|
||||
3: {
|
||||
5: "High env temp.",
|
||||
},
|
||||
},
|
||||
81: {
|
||||
0: {0: "Chip data error."},
|
||||
0: {
|
||||
0: "Chip data error.",
|
||||
},
|
||||
},
|
||||
82: {
|
||||
0: {0: "Power version error."},
|
||||
1: {0: "Miner type error."},
|
||||
2: {0: "Version info error."},
|
||||
0: {
|
||||
0: "Power version error.",
|
||||
},
|
||||
1: {
|
||||
0: "Miner type error.",
|
||||
},
|
||||
2: {
|
||||
0: "Version info error.",
|
||||
},
|
||||
},
|
||||
83: {
|
||||
0: {0: "Empty level error."},
|
||||
0: {
|
||||
0: "Empty level error.",
|
||||
},
|
||||
},
|
||||
84: {
|
||||
0: {0: "Old firmware."},
|
||||
1: {0: "Software version error."},
|
||||
0: {
|
||||
0: "Old firmware.",
|
||||
},
|
||||
1: {
|
||||
0: "Software version error.",
|
||||
},
|
||||
},
|
||||
85: {
|
||||
"n": {
|
||||
@@ -296,8 +465,12 @@ ERROR_CODES = {
|
||||
},
|
||||
},
|
||||
86: {
|
||||
0: {0: "Missing product serial #."},
|
||||
1: {0: "Missing product type."},
|
||||
0: {
|
||||
0: "Missing product serial #.",
|
||||
},
|
||||
1: {
|
||||
0: "Missing product type.",
|
||||
},
|
||||
2: {
|
||||
0: "Missing miner serial #.",
|
||||
1: "Wrong miner serial # length.",
|
||||
@@ -314,12 +487,34 @@ ERROR_CODES = {
|
||||
3: "Wrong power model rate.",
|
||||
4: "Wrong power model format.",
|
||||
},
|
||||
5: {0: "Wrong hash board struct."},
|
||||
6: {0: "Wrong miner cooling type."},
|
||||
7: {0: "Missing PCB serial #."},
|
||||
5: {
|
||||
0: "Wrong hash board struct.",
|
||||
},
|
||||
6: {
|
||||
0: "Wrong miner cooling type.",
|
||||
},
|
||||
7: {
|
||||
0: "Missing PCB serial #.",
|
||||
},
|
||||
},
|
||||
87: {
|
||||
0: {
|
||||
0: "Miner power mismatch.",
|
||||
},
|
||||
},
|
||||
90: {
|
||||
0: {
|
||||
0: "Process error, exited with signal: 3.",
|
||||
},
|
||||
1: {
|
||||
0: "Process error, exited with signal: 3.",
|
||||
},
|
||||
},
|
||||
99: {
|
||||
9: {
|
||||
9: "Miner unknown error.",
|
||||
},
|
||||
},
|
||||
87: {0: {0: "Miner power mismatch."}},
|
||||
99: {9: {9: "Miner unknown error."}},
|
||||
1000: {
|
||||
0: {
|
||||
0: "Security library error, please upgrade firmware",
|
||||
@@ -328,7 +523,11 @@ ERROR_CODES = {
|
||||
3: "/antiv/dig/pf_partial.dig illegal.",
|
||||
},
|
||||
},
|
||||
1001: {0: {0: "Security BTMiner removed, please upgrade firmware."}},
|
||||
1001: {
|
||||
0: {
|
||||
0: "Security BTMiner removed, please upgrade firmware.",
|
||||
},
|
||||
},
|
||||
1100: {
|
||||
0: {
|
||||
0: "Security illegal file, please upgrade firmware.",
|
||||
|
||||
@@ -16,31 +16,29 @@
|
||||
|
||||
import logging
|
||||
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
def init_logger():
|
||||
if PyasicSettings().logfile:
|
||||
logging.basicConfig(
|
||||
filename="logfile.txt",
|
||||
filemode="a",
|
||||
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
|
||||
datefmt="%x %X",
|
||||
)
|
||||
else:
|
||||
logging.basicConfig(
|
||||
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
|
||||
datefmt="%x %X",
|
||||
)
|
||||
# if PyasicSettings().logfile:
|
||||
# logging.basicConfig(
|
||||
# filename="logfile.txt",
|
||||
# filemode="a",
|
||||
# format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
|
||||
# datefmt="%x %X",
|
||||
# )
|
||||
# else:
|
||||
logging.basicConfig(
|
||||
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
|
||||
datefmt="%x %X",
|
||||
)
|
||||
|
||||
_logger = logging.getLogger()
|
||||
|
||||
if PyasicSettings().debug:
|
||||
_logger.setLevel(logging.DEBUG)
|
||||
logging.getLogger("asyncssh").setLevel(logging.DEBUG)
|
||||
else:
|
||||
_logger.setLevel(logging.WARNING)
|
||||
logging.getLogger("asyncssh").setLevel(logging.WARNING)
|
||||
# if PyasicSettings().debug:
|
||||
# _logger.setLevel(logging.DEBUG)
|
||||
# logging.getLogger("asyncssh").setLevel(logging.DEBUG)
|
||||
# else:
|
||||
_logger.setLevel(logging.WARNING)
|
||||
logging.getLogger("asyncssh").setLevel(logging.WARNING)
|
||||
|
||||
return _logger
|
||||
|
||||
|
||||
@@ -20,3 +20,4 @@ from .cgminer import *
|
||||
from .hiveon import *
|
||||
from .luxos import *
|
||||
from .vnish import *
|
||||
from .epic import *
|
||||
|
||||
@@ -21,9 +21,11 @@ from pyasic.miners.types import (
|
||||
S19XP,
|
||||
S19a,
|
||||
S19aPro,
|
||||
S19i,
|
||||
S19j,
|
||||
S19jNoPIC,
|
||||
S19jPro,
|
||||
S19Plus,
|
||||
S19Pro,
|
||||
S19ProPlus,
|
||||
)
|
||||
@@ -33,6 +35,14 @@ class BMMinerS19(AntminerModern, S19):
|
||||
pass
|
||||
|
||||
|
||||
class BMMinerS19Plus(AntminerModern, S19Plus):
|
||||
pass
|
||||
|
||||
|
||||
class BMMinerS19i(AntminerModern, S19i):
|
||||
pass
|
||||
|
||||
|
||||
class BMMinerS19Pro(AntminerModern, S19Pro):
|
||||
pass
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ from .S19 import (
|
||||
BMMinerS19,
|
||||
BMMinerS19a,
|
||||
BMMinerS19aPro,
|
||||
BMMinerS19i,
|
||||
BMMinerS19j,
|
||||
BMMinerS19jNoPIC,
|
||||
BMMinerS19jPro,
|
||||
BMMinerS19L,
|
||||
BMMinerS19Plus,
|
||||
BMMinerS19Pro,
|
||||
BMMinerS19ProPlus,
|
||||
BMMinerS19XP,
|
||||
|
||||
40
pyasic/miners/antminer/epic/X19/S19.py
Normal file
40
pyasic/miners/antminer/epic/X19/S19.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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.types import (
|
||||
S19,
|
||||
S19Pro,
|
||||
S19j,
|
||||
S19jPro,
|
||||
S19XP,
|
||||
)
|
||||
|
||||
|
||||
class ePICS19(ePIC, S19):
|
||||
pass
|
||||
|
||||
class ePICS19Pro(ePIC, S19Pro):
|
||||
pass
|
||||
|
||||
class ePICS19j(ePIC, S19j):
|
||||
pass
|
||||
|
||||
class ePICS19jPro(ePIC, S19jPro):
|
||||
pass
|
||||
|
||||
class ePICS19XP(ePIC, S19XP):
|
||||
pass
|
||||
23
pyasic/miners/antminer/epic/X19/__init__.py
Normal file
23
pyasic/miners/antminer/epic/X19/__init__.py
Normal 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 .S19 import (
|
||||
ePICS19,
|
||||
ePICS19Pro,
|
||||
ePICS19j,
|
||||
ePICS19jPro,
|
||||
ePICS19XP,
|
||||
)
|
||||
17
pyasic/miners/antminer/epic/__init__.py
Normal file
17
pyasic/miners/antminer/epic/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .X19 import *
|
||||
@@ -24,4 +24,5 @@ from .cgminer_avalon import CGMinerAvalon
|
||||
from .hiveon import Hiveon
|
||||
from .luxminer import LUXMiner
|
||||
from .vnish import VNish
|
||||
from .epic import ePIC
|
||||
from .whatsminer import M2X, M3X, M5X
|
||||
|
||||
@@ -109,7 +109,7 @@ class AntminerModern(BMMiner):
|
||||
data = await self.web.blink(blink=False)
|
||||
if data:
|
||||
if data.get("code") == "B100":
|
||||
self.light = True
|
||||
self.light = False
|
||||
return self.light
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
@@ -274,7 +274,11 @@ class AntminerModern(BMMiner):
|
||||
|
||||
if web_get_conf:
|
||||
try:
|
||||
return False if int(web_get_conf["bitmain-work-mode"]) == 1 else True
|
||||
if web_get_conf["bitmain-work-mode"].isdigit():
|
||||
return (
|
||||
False if int(web_get_conf["bitmain-work-mode"]) == 1 else True
|
||||
)
|
||||
return False
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -235,7 +235,22 @@ class BMMiner(BaseMiner):
|
||||
if board_offset == -1:
|
||||
board_offset = 1
|
||||
|
||||
for i in range(board_offset, board_offset + self.ideal_hashboards):
|
||||
real_slots = []
|
||||
|
||||
for i in range(board_offset, board_offset + 4):
|
||||
try:
|
||||
key = f"chain_acs{i}"
|
||||
if boards[1].get(key, "") != "":
|
||||
real_slots.append(i)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
if len(real_slots) < 3:
|
||||
real_slots = list(
|
||||
range(board_offset, board_offset + self.ideal_hashboards)
|
||||
)
|
||||
|
||||
for i in real_slots:
|
||||
hashboard = HashBoard(
|
||||
slot=i - board_offset, expected_chips=self.nominal_chips
|
||||
)
|
||||
@@ -259,7 +274,7 @@ class BMMiner(BaseMiner):
|
||||
if (not chips) or (not chips > 0):
|
||||
hashboard.missing = True
|
||||
hashboards.append(hashboard)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
except (LookupError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
return hashboards
|
||||
|
||||
@@ -184,11 +184,11 @@ BOSMINER_DATA_LOC = {
|
||||
|
||||
|
||||
class BOSMiner(BaseMiner):
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0", boser: bool = None) -> None:
|
||||
super().__init__(ip)
|
||||
# interfaces
|
||||
self.api = BOSMinerAPI(ip, api_ver)
|
||||
self.web = BOSMinerWebAPI(ip)
|
||||
self.web = BOSMinerWebAPI(ip, boser=boser)
|
||||
|
||||
# static data
|
||||
self.api_type = "BOSMiner"
|
||||
@@ -303,17 +303,12 @@ class BOSMiner(BaseMiner):
|
||||
The config from `self.config`.
|
||||
"""
|
||||
logging.debug(f"{self}: Getting config.")
|
||||
conn = None
|
||||
|
||||
try:
|
||||
conn = await self._get_ssh_connection()
|
||||
except ConnectionError:
|
||||
try:
|
||||
pools = await self.api.pools()
|
||||
except APIError:
|
||||
return self.config
|
||||
if pools:
|
||||
self.config = MinerConfig().from_api(pools["POOLS"])
|
||||
return self.config
|
||||
conn = None
|
||||
|
||||
if conn:
|
||||
async with conn:
|
||||
# good ol' BBB compatibility :/
|
||||
@@ -365,6 +360,8 @@ class BOSMiner(BaseMiner):
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
try:
|
||||
cfg = await self.get_config()
|
||||
if cfg is None:
|
||||
return False
|
||||
cfg.autotuning_wattage = wattage
|
||||
await self.send_config(cfg)
|
||||
except Exception as e:
|
||||
@@ -790,7 +787,7 @@ class BOSMiner(BaseMiner):
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if graphql_fans:
|
||||
if graphql_fans.get("data"):
|
||||
fans = []
|
||||
for n in range(self.fan_count):
|
||||
try:
|
||||
@@ -1046,7 +1043,7 @@ class BOSMiner(BaseMiner):
|
||||
if data == "50":
|
||||
self.light = True
|
||||
return self.light
|
||||
except TypeError:
|
||||
except (TypeError, AttributeError):
|
||||
return self.light
|
||||
|
||||
async def get_nominal_hashrate(self, api_devs: dict = None) -> Optional[float]:
|
||||
|
||||
@@ -442,7 +442,8 @@ class BTMiner(BaseMiner):
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return api_summary["SUMMARY"][0]["Power"]
|
||||
wattage = api_summary["SUMMARY"][0]["Power"]
|
||||
return wattage if not wattage == -1 else None
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
|
||||
@@ -179,11 +179,12 @@ class CGMinerAvalon(CGMiner):
|
||||
pass
|
||||
|
||||
async def get_hostname(self, mac: str = None) -> Optional[str]:
|
||||
if not mac:
|
||||
mac = await self.get_mac()
|
||||
|
||||
if mac:
|
||||
return f"Avalon{mac.replace(':', '')[-6:]}"
|
||||
return None
|
||||
# if not mac:
|
||||
# mac = await self.get_mac()
|
||||
#
|
||||
# if mac:
|
||||
# return f"Avalon{mac.replace(':', '')[-6:]}"
|
||||
|
||||
async def get_hashrate(self, api_devs: dict = None) -> Optional[float]:
|
||||
if not api_devs:
|
||||
|
||||
302
pyasic/miners/backends/epic.py
Normal file
302
pyasic/miners/backends/epic.py
Normal file
@@ -0,0 +1,302 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.logger import logger
|
||||
from pyasic.miners.backends.bmminer import BMMiner
|
||||
from pyasic.web.epic import ePICWebAPI
|
||||
from pyasic.data import Fan, HashBoard
|
||||
from typing import List, Optional, Tuple, Union
|
||||
from pyasic.data.error_codes import MinerErrorData, X19Error
|
||||
|
||||
|
||||
EPIC_DATA_LOC = {
|
||||
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "network"}}},
|
||||
"model": {"cmd": "get_model", "kwargs": {}},
|
||||
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
|
||||
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"hashrate": {"cmd": "get_hashrate", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"nominal_hashrate": {
|
||||
"cmd": "get_nominal_hashrate",
|
||||
"kwargs": {"web_summary": {"web": "summary"}},
|
||||
},
|
||||
"hashboards": {"cmd": "get_hashboards", "kwargs": {"web_summary": {"web": "summary"}, "web_hashrate": {"web": "hashrate"}}},
|
||||
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
|
||||
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"fans": {"cmd": "get_fans", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
|
||||
"fault_light": {"cmd": "get_fault_light", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"pools": {"cmd": "get_pools", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"is_mining": {"cmd": "is_mining", "kwargs": {}},
|
||||
"uptime": {"cmd": "get_uptime", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
|
||||
}
|
||||
|
||||
|
||||
class ePIC(BMMiner):
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip, api_ver)
|
||||
# interfaces
|
||||
self.web = ePICWebAPI(ip)
|
||||
|
||||
# static data
|
||||
self.api_type = "ePIC"
|
||||
# data gathering locations
|
||||
self.data_locations = EPIC_DATA_LOC
|
||||
|
||||
async def get_model(self) -> Optional[str]:
|
||||
if self.model is not None:
|
||||
return self.model + " (ePIC)"
|
||||
return "? (ePIC)"
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
data = await self.web.restart_epic()
|
||||
if data:
|
||||
try:
|
||||
return data["success"]
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def stop_mining(self) -> bool:
|
||||
data = await self.web.stop_mining()
|
||||
if data:
|
||||
try:
|
||||
return data["success"]
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def resume_mining(self) -> bool:
|
||||
data = await self.web.resume_mining()
|
||||
if data:
|
||||
try:
|
||||
return data["success"]
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
data = await self.web.reboot()
|
||||
if data:
|
||||
try:
|
||||
return data["success"]
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def get_mac(self, web_summary: dict = None) -> str:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.network()
|
||||
if web_summary:
|
||||
try:
|
||||
for network in web_summary:
|
||||
mac = web_summary[network]["mac_address"]
|
||||
return mac
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def get_hostname(self, web_summary: dict = None) -> str:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.summary()
|
||||
if web_summary:
|
||||
try:
|
||||
hostname = web_summary["Hostname"]
|
||||
return hostname
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def get_wattage(self, web_summary: dict = None) -> Optional[int]:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.summary()
|
||||
|
||||
if web_summary:
|
||||
try:
|
||||
wattage = web_summary["Power Supply Stats"]["Input Power"]
|
||||
wattage = round(wattage)
|
||||
return wattage
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def get_hashrate(self, web_summary: dict = None) -> Optional[float]:
|
||||
# get hr from API
|
||||
if not web_summary:
|
||||
try:
|
||||
web_summary = await self.web.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_summary:
|
||||
try:
|
||||
hashrate = 0
|
||||
if web_summary["HBs"] != None:
|
||||
for hb in web_summary["HBs"]:
|
||||
hashrate += hb["Hashrate"][0]
|
||||
return round(
|
||||
float(float(hashrate/ 1000000)), 2)
|
||||
except (LookupError, ValueError, TypeError) as e:
|
||||
logger.error(e)
|
||||
pass
|
||||
|
||||
async def get_nominal_hashrate(self, web_summary: dict = None) -> Optional[float]:
|
||||
# get hr from API
|
||||
if not web_summary:
|
||||
try:
|
||||
web_summary = await self.web.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_summary:
|
||||
try:
|
||||
hashrate = 0
|
||||
if web_summary["HBs"] != None:
|
||||
for hb in web_summary["HBs"]:
|
||||
if hb["Hashrate"][1] == 0:
|
||||
ideal = 1.0
|
||||
else:
|
||||
ideal = hb["Hashrate"][1]/100
|
||||
|
||||
hashrate += hb["Hashrate"][0]/ideal
|
||||
return round(
|
||||
float(float(hashrate/ 1000000)), 2)
|
||||
except (IndexError, KeyError, ValueError, TypeError) as e:
|
||||
logger.error(e)
|
||||
pass
|
||||
|
||||
|
||||
async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.summary()
|
||||
|
||||
if web_summary:
|
||||
try:
|
||||
fw_ver = web_summary["Software"]
|
||||
fw_ver = fw_ver.split(" ")[1].replace("v", "")
|
||||
return fw_ver
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def get_fans(self, web_summary: dict = None) -> List[Fan]:
|
||||
if not web_summary:
|
||||
try:
|
||||
web_summary = await self.web.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
fans = []
|
||||
|
||||
if web_summary:
|
||||
for fan in web_summary["Fans Rpm"]:
|
||||
try:
|
||||
fans.append(Fan(web_summary["Fans Rpm"][fan]))
|
||||
except (LookupError, ValueError, TypeError):
|
||||
fans.append(Fan())
|
||||
return fans
|
||||
|
||||
async def get_hashboards(self, web_summary: dict = None, web_hashrate: dict= None) -> List[HashBoard]:
|
||||
if not web_summary:
|
||||
try:
|
||||
web_summary = await self.web.summary()
|
||||
except APIError:
|
||||
pass
|
||||
if not web_hashrate:
|
||||
try:
|
||||
web_hashrate = await self.web.hashrate()
|
||||
except APIError:
|
||||
pass
|
||||
hb_list = [HashBoard(slot=i, expected_chips=self.nominal_chips) for i in range(self.ideal_hashboards)]
|
||||
if web_summary["HBs"] != None:
|
||||
for hb in web_summary["HBs"]:
|
||||
for hr in web_hashrate:
|
||||
if hr["Index"] == hb["Index"]:
|
||||
num_of_chips = len(hr["Data"])
|
||||
hashrate = hb["Hashrate"][0]
|
||||
#Update the Hashboard object
|
||||
hb_list[hr["Index"]].expected_chips = num_of_chips
|
||||
hb_list[hr["Index"]].missing = False
|
||||
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
|
||||
|
||||
async def is_mining(self, *args, **kwargs) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
async def get_pools(self, web_summary: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
|
||||
if not web_summary:
|
||||
try:
|
||||
web_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_summary:
|
||||
try:
|
||||
pools = {}
|
||||
for i, pool in enumerate(web_summary["StratumConfigs"]):
|
||||
pools[f"pool_{i + 1}_url"] = (
|
||||
pool["pool"]
|
||||
.replace("stratum+tcp://", "")
|
||||
.replace("stratum2+tcp://", "")
|
||||
)
|
||||
pools[f"pool_{i + 1}_user"] = pool["login"]
|
||||
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
|
||||
|
||||
groups.append(pools)
|
||||
except KeyError:
|
||||
pass
|
||||
return groups
|
||||
|
||||
|
||||
async def get_uptime(self, web_summary: dict = None) -> Optional[int]:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.summary()
|
||||
if web_summary:
|
||||
try:
|
||||
uptime = web_summary["Session"]["Uptime"]
|
||||
return uptime
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
||||
async def get_fault_light(self, web_summary: dict = None) -> bool:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.summary()
|
||||
if web_summary:
|
||||
try:
|
||||
light = web_summary["Misc"]["Locate Miner State"]
|
||||
return light
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
|
||||
if not web_summary:
|
||||
web_summary = await self.web.summary()
|
||||
errors = []
|
||||
if web_summary:
|
||||
try:
|
||||
error = web_summary["Status"]["Last Error"]
|
||||
if error != None:
|
||||
errors.append(X19Error(str(error)))
|
||||
return errors
|
||||
except KeyError:
|
||||
pass
|
||||
return errors
|
||||
@@ -413,21 +413,29 @@ class BaseMiner(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
async def _get_data(self, allow_warning: bool, data_to_get: list = None) -> dict:
|
||||
if not data_to_get:
|
||||
async def _get_data(
|
||||
self, allow_warning: bool, include: list = None, exclude: list = None
|
||||
) -> dict:
|
||||
if include is None:
|
||||
# everything
|
||||
data_to_get = list(self.data_locations.keys())
|
||||
include = list(self.data_locations.keys())
|
||||
|
||||
if exclude is not None:
|
||||
for item in exclude:
|
||||
if item in include:
|
||||
include.remove(item)
|
||||
|
||||
api_multicommand = set()
|
||||
web_multicommand = set()
|
||||
for data_name in data_to_get:
|
||||
web_multicommand = []
|
||||
for data_name in include:
|
||||
try:
|
||||
fn_args = self.data_locations[data_name]["kwargs"]
|
||||
for arg_name in fn_args:
|
||||
if fn_args[arg_name].get("api"):
|
||||
api_multicommand.add(fn_args[arg_name]["api"])
|
||||
if fn_args[arg_name].get("web"):
|
||||
web_multicommand.add(fn_args[arg_name]["web"])
|
||||
if not fn_args[arg_name]["web"] in web_multicommand:
|
||||
web_multicommand.append(fn_args[arg_name]["web"])
|
||||
except KeyError as e:
|
||||
logger.error(e, data_name)
|
||||
continue
|
||||
@@ -445,8 +453,6 @@ class BaseMiner(ABC):
|
||||
else:
|
||||
web_command_task = asyncio.sleep(0)
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
web_command_data = await web_command_task
|
||||
if web_command_data is None:
|
||||
web_command_data = {}
|
||||
@@ -457,7 +463,7 @@ class BaseMiner(ABC):
|
||||
|
||||
miner_data = {}
|
||||
|
||||
for data_name in data_to_get:
|
||||
for data_name in include:
|
||||
try:
|
||||
fn_args = self.data_locations[data_name]["kwargs"]
|
||||
args_to_send = {k: None for k in fn_args}
|
||||
@@ -511,13 +517,14 @@ class BaseMiner(ABC):
|
||||
return miner_data
|
||||
|
||||
async def get_data(
|
||||
self, allow_warning: bool = False, data_to_get: list = None
|
||||
self, allow_warning: bool = False, include: list = None, exclude: list = None
|
||||
) -> MinerData:
|
||||
"""Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].
|
||||
|
||||
Parameters:
|
||||
allow_warning: Allow warning when an API command fails.
|
||||
data_to_get: Names of data items you want to gather. Defaults to all data.
|
||||
include: Names of data items you want to gather. Defaults to all data.
|
||||
exclude: Names of data items to exclude. Exclusion happens after considering included items.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing data from the miner.
|
||||
@@ -533,7 +540,9 @@ class BaseMiner(ABC):
|
||||
],
|
||||
)
|
||||
|
||||
gathered_data = await self._get_data(allow_warning, data_to_get=data_to_get)
|
||||
gathered_data = await self._get_data(
|
||||
allow_warning=allow_warning, include=include, exclude=exclude
|
||||
)
|
||||
for item in gathered_data:
|
||||
if gathered_data[item] is not None:
|
||||
setattr(data, item, gathered_data[item])
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import asyncio
|
||||
import enum
|
||||
import ipaddress
|
||||
import json
|
||||
import re
|
||||
from typing import Callable, List, Optional, Tuple, Union
|
||||
from typing import AsyncGenerator, Callable, List, Optional, Tuple, Union
|
||||
|
||||
import anyio
|
||||
import httpx
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.logger import logger
|
||||
from pyasic.miners.antminer import *
|
||||
from pyasic.miners.avalonminer import *
|
||||
@@ -37,6 +37,7 @@ from pyasic.miners.backends import (
|
||||
Hiveon,
|
||||
LUXMiner,
|
||||
VNish,
|
||||
ePIC,
|
||||
)
|
||||
from pyasic.miners.base import AnyMiner
|
||||
from pyasic.miners.goldshell import *
|
||||
@@ -44,9 +45,6 @@ from pyasic.miners.innosilicon import *
|
||||
from pyasic.miners.unknown import UnknownMiner
|
||||
from pyasic.miners.whatsminer import *
|
||||
|
||||
TIMEOUT = 20
|
||||
RETRIES = 3
|
||||
|
||||
|
||||
class MinerTypes(enum.Enum):
|
||||
ANTMINER = 0
|
||||
@@ -58,6 +56,7 @@ class MinerTypes(enum.Enum):
|
||||
VNISH = 6
|
||||
HIVEON = 7
|
||||
LUX_OS = 8
|
||||
EPIC = 9
|
||||
|
||||
|
||||
MINER_CLASSES = {
|
||||
@@ -85,6 +84,8 @@ MINER_CLASSES = {
|
||||
"ANTMINER S19L": BMMinerS19L,
|
||||
"ANTMINER S19 PRO": BMMinerS19Pro,
|
||||
"ANTMINER S19J": BMMinerS19j,
|
||||
"ANTMINER S19I": BMMinerS19i,
|
||||
"ANTMINER S19+": BMMinerS19Plus,
|
||||
"ANTMINER S19J88NOPIC": BMMinerS19jNoPIC,
|
||||
"ANTMINER S19PRO+": BMMinerS19ProPlus,
|
||||
"ANTMINER S19J PRO": BMMinerS19jPro,
|
||||
@@ -99,6 +100,8 @@ MINER_CLASSES = {
|
||||
"M20SV10": BTMinerM20SV10,
|
||||
"M20SV20": BTMinerM20SV20,
|
||||
"M20SV30": BTMinerM20SV30,
|
||||
"M20PV10": BTMinerM20PV10,
|
||||
"M20PV30": BTMinerM20PV30,
|
||||
"M20S+V30": BTMinerM20SPlusV30,
|
||||
"M21V10": BTMinerM21V10,
|
||||
"M21SV20": BTMinerM21SV20,
|
||||
@@ -108,6 +111,8 @@ MINER_CLASSES = {
|
||||
"M29V10": BTMinerM29V10,
|
||||
"M30V10": BTMinerM30V10,
|
||||
"M30V20": BTMinerM30V20,
|
||||
"M30KV10": BTMinerM30KV10,
|
||||
"M30LV10": BTMinerM30LV10,
|
||||
"M30SV10": BTMinerM30SV10,
|
||||
"M30SV20": BTMinerM30SV20,
|
||||
"M30SV30": BTMinerM30SV30,
|
||||
@@ -157,6 +162,7 @@ MINER_CLASSES = {
|
||||
"M30S+VE100": BTMinerM30SPlusVE100,
|
||||
"M30S+VF20": BTMinerM30SPlusVF20,
|
||||
"M30S+VF30": BTMinerM30SPlusVF30,
|
||||
"M30S+VG20": BTMinerM30SPlusVG20,
|
||||
"M30S+VG30": BTMinerM30SPlusVG30,
|
||||
"M30S+VG40": BTMinerM30SPlusVG40,
|
||||
"M30S+VG50": BTMinerM30SPlusVG50,
|
||||
@@ -190,6 +196,9 @@ MINER_CLASSES = {
|
||||
"M30S++VJ30": BTMinerM30SPlusPlusVJ30,
|
||||
"M31V10": BTMinerM31V10,
|
||||
"M31V20": BTMinerM31V20,
|
||||
"M31HV10": BTMinerM31HV10,
|
||||
"M31HV40": BTMinerM31HV40,
|
||||
"M31LV10": BTMinerM31LV10,
|
||||
"M31SV10": BTMinerM31SV10,
|
||||
"M31SV20": BTMinerM31SV20,
|
||||
"M31SV30": BTMinerM31SV30,
|
||||
@@ -205,7 +214,6 @@ MINER_CLASSES = {
|
||||
"M31SEV10": BTMinerM31SEV10,
|
||||
"M31SEV20": BTMinerM31SEV20,
|
||||
"M31SEV30": BTMinerM31SEV30,
|
||||
"M31HV40": BTMinerM31HV40,
|
||||
"M31S+V10": BTMinerM31SPlusV10,
|
||||
"M31S+V20": BTMinerM31SPlusV20,
|
||||
"M31S+V30": BTMinerM31SPlusV30,
|
||||
@@ -232,6 +240,7 @@ MINER_CLASSES = {
|
||||
"M33V20": BTMinerM33V20,
|
||||
"M33V30": BTMinerM33V30,
|
||||
"M33SVG30": BTMinerM33SVG30,
|
||||
"M33S+VG20": BTMinerM33SPlusVG20,
|
||||
"M33S+VH20": BTMinerM33SPlusVH20,
|
||||
"M33S+VH30": BTMinerM33SPlusVH30,
|
||||
"M33S++VH20": BTMinerM33SPlusPlusVH20,
|
||||
@@ -241,7 +250,10 @@ MINER_CLASSES = {
|
||||
"M36SVE10": BTMinerM36SVE10,
|
||||
"M36S+VG30": BTMinerM36SPlusVG30,
|
||||
"M36S++VH30": BTMinerM36SPlusPlusVH30,
|
||||
"M39V10": BTMinerM39V10,
|
||||
"M39V20": BTMinerM39V20,
|
||||
"M39V30": BTMinerM39V30,
|
||||
"M50VE30": BTMinerM50VE30,
|
||||
"M50VG30": BTMinerM50VG30,
|
||||
"M50VH10": BTMinerM50VH10,
|
||||
"M50VH20": BTMinerM50VH20,
|
||||
@@ -336,6 +348,14 @@ MINER_CLASSES = {
|
||||
"ANTMINER S19A PRO": VNishS19aPro,
|
||||
"ANTMINER T19": VNishT19,
|
||||
},
|
||||
MinerTypes.EPIC: {
|
||||
None: ePIC,
|
||||
"ANTMINER S19": ePICS19,
|
||||
"ANTMINER S19 PRO": ePICS19Pro,
|
||||
"ANTMINER S19J": ePICS19j,
|
||||
"ANTMINER S19J PRO": ePICS19jPro,
|
||||
"ANTMINER S19 XP": ePICS19XP,
|
||||
},
|
||||
MinerTypes.HIVEON: {
|
||||
None: Hiveon,
|
||||
"ANTMINER T9": HiveonT9,
|
||||
@@ -375,7 +395,9 @@ class MinerFactory:
|
||||
def clear_cached_miners(self):
|
||||
self.cache = {}
|
||||
|
||||
async def get_multiple_miners(self, ips: List[str], limit: int = 200):
|
||||
async def get_multiple_miners(
|
||||
self, ips: List[str], limit: int = 200
|
||||
) -> List[AnyMiner]:
|
||||
results = []
|
||||
|
||||
async for miner in self.get_miner_generator(ips, limit):
|
||||
@@ -383,7 +405,7 @@ class MinerFactory:
|
||||
|
||||
return results
|
||||
|
||||
async def get_miner_generator(self, ips: list, limit: int = 200):
|
||||
async def get_miner_generator(self, ips: list, limit: int = 200) -> AsyncGenerator:
|
||||
tasks = []
|
||||
semaphore = asyncio.Semaphore(limit)
|
||||
|
||||
@@ -391,13 +413,10 @@ class MinerFactory:
|
||||
tasks.append(asyncio.create_task(self.get_miner(ip)))
|
||||
|
||||
for task in tasks:
|
||||
await semaphore.acquire()
|
||||
try:
|
||||
async with semaphore:
|
||||
result = await task
|
||||
if result is not None:
|
||||
yield result
|
||||
finally:
|
||||
semaphore.release()
|
||||
|
||||
async def get_miner(self, ip: str):
|
||||
ip = str(ip)
|
||||
@@ -406,10 +425,12 @@ class MinerFactory:
|
||||
|
||||
miner_type = None
|
||||
|
||||
for _ in range(RETRIES):
|
||||
for _ in range(settings.get("factory_get_retries", 1)):
|
||||
task = asyncio.create_task(self._get_miner_type(ip))
|
||||
try:
|
||||
miner_type = await asyncio.wait_for(task, timeout=TIMEOUT)
|
||||
miner_type = await asyncio.wait_for(
|
||||
task, timeout=settings.get("factory_get_timeout", 3)
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
task.cancel()
|
||||
else:
|
||||
@@ -426,6 +447,7 @@ class MinerFactory:
|
||||
MinerTypes.GOLDSHELL: self.get_miner_model_goldshell,
|
||||
MinerTypes.BRAIINS_OS: self.get_miner_model_braiins_os,
|
||||
MinerTypes.VNISH: self.get_miner_model_vnish,
|
||||
MinerTypes.EPIC: self.get_miner_model_epic,
|
||||
MinerTypes.HIVEON: self.get_miner_model_hiveon,
|
||||
MinerTypes.LUX_OS: self.get_miner_model_luxos,
|
||||
}
|
||||
@@ -434,12 +456,21 @@ class MinerFactory:
|
||||
if fn is not None:
|
||||
task = asyncio.create_task(fn(ip))
|
||||
try:
|
||||
miner_model = await asyncio.wait_for(task, timeout=30)
|
||||
miner_model = await asyncio.wait_for(
|
||||
task, timeout=settings.get("factory_get_timeout", 3)
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
task.cancel()
|
||||
|
||||
boser_enabled = None
|
||||
if miner_type == MinerTypes.BRAIINS_OS:
|
||||
boser_enabled = await self.get_boser_braiins_os(ip)
|
||||
|
||||
miner = self._select_miner_from_classes(
|
||||
ip, miner_type=miner_type, miner_model=miner_model
|
||||
ip,
|
||||
miner_type=miner_type,
|
||||
miner_model=miner_model,
|
||||
boser_enabled=boser_enabled,
|
||||
)
|
||||
|
||||
if miner is not None and not isinstance(miner, UnknownMiner):
|
||||
@@ -449,18 +480,22 @@ class MinerFactory:
|
||||
async def _get_miner_type(self, ip: str):
|
||||
tasks = [
|
||||
asyncio.create_task(self._get_miner_web(ip)),
|
||||
asyncio.create_task(self._get_miner_socket(ip)),
|
||||
# asyncio.create_task(self._get_miner_socket(ip)),
|
||||
]
|
||||
|
||||
return await concurrent_get_first_result(tasks, lambda x: x is not None)
|
||||
|
||||
async def _get_miner_web(self, ip: str):
|
||||
urls = [f"http://{ip}/", f"https://{ip}/"]
|
||||
async with httpx.AsyncClient(verify=False) as session:
|
||||
async with httpx.AsyncClient(
|
||||
transport=settings.transport(verify=False)
|
||||
) as session:
|
||||
tasks = [asyncio.create_task(self._web_ping(session, url)) for url in urls]
|
||||
|
||||
text, resp = await concurrent_get_first_result(
|
||||
tasks, lambda x: x[0] is not None
|
||||
tasks,
|
||||
lambda x: x[0] is not None
|
||||
and self._parse_web_type(x[0], x[1]) is not None,
|
||||
)
|
||||
if text is not None:
|
||||
return self._parse_web_type(text, resp)
|
||||
@@ -470,9 +505,14 @@ class MinerFactory:
|
||||
session: httpx.AsyncClient, url: str
|
||||
) -> Tuple[Optional[str], Optional[httpx.Response]]:
|
||||
try:
|
||||
resp = await session.get(url, follow_redirects=False)
|
||||
resp = await session.get(url, follow_redirects=True)
|
||||
return resp.text, resp
|
||||
except (httpx.HTTPError, asyncio.TimeoutError):
|
||||
except (
|
||||
httpx.HTTPError,
|
||||
asyncio.TimeoutError,
|
||||
anyio.EndOfStream,
|
||||
anyio.ClosedResourceError,
|
||||
):
|
||||
pass
|
||||
return None, None
|
||||
|
||||
@@ -482,16 +522,22 @@ class MinerFactory:
|
||||
"www-authenticate", ""
|
||||
):
|
||||
return MinerTypes.ANTMINER
|
||||
if web_resp.status_code == 307 and "https://" in web_resp.headers.get(
|
||||
"location", ""
|
||||
):
|
||||
return MinerTypes.WHATSMINER
|
||||
if "Braiins OS" in web_text or 'href="/cgi-bin/luci"' in web_text:
|
||||
if len(web_resp.history) > 0:
|
||||
history_resp = web_resp.history[0]
|
||||
if (
|
||||
"/cgi-bin/luci" in web_text
|
||||
and history_resp.status_code == 307
|
||||
and "https://" in history_resp.headers.get("location", "")
|
||||
):
|
||||
return MinerTypes.WHATSMINER
|
||||
if "Braiins OS" in web_text:
|
||||
return MinerTypes.BRAIINS_OS
|
||||
if "cloud-box" in web_text:
|
||||
return MinerTypes.GOLDSHELL
|
||||
if "AnthillOS" in web_text:
|
||||
return MinerTypes.VNISH
|
||||
if "Miner Web Dashboard" in web_text:
|
||||
return MinerTypes.EPIC
|
||||
if "Avalon" in web_text:
|
||||
return MinerTypes.AVALONMINER
|
||||
if "DragonMint" in web_text:
|
||||
@@ -513,7 +559,8 @@ class MinerFactory:
|
||||
data = b""
|
||||
try:
|
||||
reader, writer = await asyncio.wait_for(
|
||||
asyncio.open_connection(str(ip), 4028), timeout=30
|
||||
asyncio.open_connection(str(ip), 4028),
|
||||
timeout=settings.get("factory_get_timeout", 3),
|
||||
)
|
||||
except (ConnectionError, OSError, asyncio.TimeoutError):
|
||||
return
|
||||
@@ -579,12 +626,12 @@ class MinerFactory:
|
||||
location: str,
|
||||
auth: Optional[httpx.DigestAuth] = None,
|
||||
) -> Optional[dict]:
|
||||
async with httpx.AsyncClient(verify=False) as session:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as session:
|
||||
try:
|
||||
data = await session.get(
|
||||
f"http://{str(ip)}{location}",
|
||||
auth=auth,
|
||||
timeout=30,
|
||||
timeout=settings.get("factory_get_timeout", 3),
|
||||
)
|
||||
except (httpx.HTTPError, asyncio.TimeoutError):
|
||||
logger.info(f"{ip}: Web command timeout.")
|
||||
@@ -683,9 +730,13 @@ class MinerFactory:
|
||||
ip: ipaddress.ip_address,
|
||||
miner_model: Union[str, None],
|
||||
miner_type: Union[MinerTypes, None],
|
||||
boser_enabled: bool = None,
|
||||
) -> AnyMiner:
|
||||
kwargs = {}
|
||||
if boser_enabled is not None:
|
||||
kwargs["boser"] = boser_enabled
|
||||
try:
|
||||
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
|
||||
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip, **kwargs)
|
||||
except LookupError:
|
||||
if miner_type in MINER_CLASSES:
|
||||
return MINER_CLASSES[miner_type][None](ip)
|
||||
@@ -751,7 +802,8 @@ class MinerFactory:
|
||||
async def get_miner_model_whatsminer(self, ip: str):
|
||||
sock_json_data = await self.send_api_command(ip, "devdetails")
|
||||
try:
|
||||
miner_model = sock_json_data["DEVDETAILS"][0]["Model"]
|
||||
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace("_", "")
|
||||
miner_model = miner_model[:-1] + "0"
|
||||
|
||||
return miner_model
|
||||
except (TypeError, LookupError):
|
||||
@@ -770,14 +822,14 @@ class MinerFactory:
|
||||
|
||||
async def get_miner_model_innosilicon(self, ip: str) -> Optional[str]:
|
||||
try:
|
||||
async with httpx.AsyncClient(verify=False) as session:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as session:
|
||||
auth_req = await session.post(
|
||||
f"http://{ip}/api/auth",
|
||||
data={"username": "admin", "password": "admin"},
|
||||
)
|
||||
auth = (await auth_req.json())["jwt"]
|
||||
auth = auth_req.json()["jwt"]
|
||||
|
||||
web_data = await (
|
||||
web_data = (
|
||||
await session.post(
|
||||
f"http://{ip}/api/type",
|
||||
headers={"Authorization": "Bearer " + auth},
|
||||
@@ -800,18 +852,27 @@ class MinerFactory:
|
||||
pass
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(verify=False) as session:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as session:
|
||||
d = await session.post(
|
||||
f"http://{ip}/graphql",
|
||||
json={"query": "{bosminer {info{modelName}}}"},
|
||||
)
|
||||
if d.status_code == 200:
|
||||
json_data = await d.json()
|
||||
json_data = d.json()
|
||||
miner_model = json_data["data"]["bosminer"]["info"]["modelName"]
|
||||
return miner_model
|
||||
except (httpx.HTTPError, LookupError):
|
||||
pass
|
||||
|
||||
async def get_boser_braiins_os(self, ip: str):
|
||||
# TODO: refine this check
|
||||
try:
|
||||
sock_json_data = await self.send_api_command(ip, "version")
|
||||
return sock_json_data["STATUS"][0]["Msg"].split(" ")[0].upper() == "BOSER"
|
||||
except LookupError:
|
||||
# let the bosminer class decide
|
||||
return None
|
||||
|
||||
async def get_miner_model_vnish(self, ip: str) -> Optional[str]:
|
||||
sock_json_data = await self.send_api_command(ip, "stats")
|
||||
try:
|
||||
@@ -829,6 +890,14 @@ class MinerFactory:
|
||||
return miner_model
|
||||
except (TypeError, LookupError):
|
||||
pass
|
||||
|
||||
async def get_miner_model_epic(self, ip: str) -> Optional[str]:
|
||||
sock_json_data = await self.send_web_command(ip, ":4028/capabilities")
|
||||
try:
|
||||
miner_model = sock_json_data["Model"]
|
||||
return miner_model
|
||||
except (TypeError, LookupError):
|
||||
pass
|
||||
|
||||
async def get_miner_model_hiveon(self, ip: str) -> Optional[str]:
|
||||
sock_json_data = await self.send_api_command(ip, "version")
|
||||
|
||||
@@ -44,6 +44,24 @@ class S19Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||
self.fan_count = 4
|
||||
|
||||
|
||||
class S19i(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "S19i"
|
||||
self.nominal_chips = 80
|
||||
self.fan_count = 4
|
||||
|
||||
|
||||
class S19Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "S19+"
|
||||
self.nominal_chips = 80
|
||||
self.fan_count = 4
|
||||
|
||||
|
||||
class S19ProPlus(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
|
||||
@@ -20,10 +20,12 @@ from .S19 import (
|
||||
S19XP,
|
||||
S19a,
|
||||
S19aPro,
|
||||
S19i,
|
||||
S19j,
|
||||
S19jNoPIC,
|
||||
S19jPro,
|
||||
S19NoPIC,
|
||||
S19Plus,
|
||||
S19Pro,
|
||||
S19ProPlus,
|
||||
)
|
||||
|
||||
35
pyasic/miners/types/whatsminer/M2X/M20P.py
Normal file
35
pyasic/miners/types/whatsminer/M2X/M20P.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 WhatsMiner
|
||||
|
||||
|
||||
class M20PV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M20P V10"
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20PV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M20P V30"
|
||||
self.nominal_chips = 148
|
||||
self.fan_count = 2
|
||||
@@ -42,8 +42,5 @@ class M20SV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M20S V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M20SV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 140
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -24,8 +24,5 @@ class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M21 V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M21V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 33
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -42,8 +42,5 @@ class M21SV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M21S V70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M21SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -24,8 +24,5 @@ class M29V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M29 V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M29V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 50
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .M20 import M20V10
|
||||
from .M20P import M20PV10, M20PV30
|
||||
from .M20S import M20SV10, M20SV20, M20SV30
|
||||
from .M20S_Plus import M20SPlusV30
|
||||
from .M21 import M21V10
|
||||
|
||||
@@ -24,10 +24,7 @@ class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30 V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,8 +33,5 @@ class M30V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30 V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
29
pyasic/miners/types/whatsminer/M3X/M30K.py
Normal file
29
pyasic/miners/types/whatsminer/M3X/M30K.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
|
||||
class M30KV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30K V10"
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 240
|
||||
self.fan_count = 2
|
||||
29
pyasic/miners/types/whatsminer/M3X/M30L.py
Normal file
29
pyasic/miners/types/whatsminer/M3X/M30L.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
|
||||
class M30LV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30L V10"
|
||||
self.board_num = 4
|
||||
self.nominal_chips = 144
|
||||
self.fan_count = 2
|
||||
@@ -24,10 +24,7 @@ class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 148
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,10 +33,7 @@ class M30SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -48,10 +42,7 @@ class M30SV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 164
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -60,10 +51,7 @@ class M30SV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S V40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 172
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -81,10 +69,7 @@ class M30SV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S V60"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 164
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -105,10 +90,7 @@ class M30SV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S V80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 129
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -135,10 +117,7 @@ class M30SVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VE30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -147,10 +126,7 @@ class M30SVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VE40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVE40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 123
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -159,10 +135,7 @@ class M30SVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VE50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 129
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -195,10 +168,7 @@ class M30SVF10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VF10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVF10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -207,10 +177,7 @@ class M30SVF20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VF20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVF20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 74
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -219,10 +186,7 @@ class M30SVF30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VF30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVF30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -249,10 +213,7 @@ class M30SVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 74
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -261,10 +222,7 @@ class M30SVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VG40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVG40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -273,10 +231,7 @@ class M30SVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VH10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 64
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -285,10 +240,7 @@ class M30SVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VH20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -309,10 +261,7 @@ class M30SVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VH40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 64
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -321,10 +270,7 @@ class M30SVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VH50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVH50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -345,8 +291,5 @@ class M30SVI20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S VI20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVI20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -24,10 +24,7 @@ class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 215
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,10 +33,7 @@ class M30SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 255
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -60,10 +54,7 @@ class M30SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 235
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -72,10 +63,7 @@ class M30SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 225
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -84,10 +72,7 @@ class M30SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V60"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 245
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -96,10 +81,7 @@ class M30SPlusV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 235
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -108,10 +90,7 @@ class M30SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 245
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -120,10 +99,7 @@ class M30SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V90"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 225
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -132,10 +108,7 @@ class M30SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V100"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 215
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -144,10 +117,7 @@ class M30SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 148
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -165,10 +135,7 @@ class M30SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 164
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -177,10 +144,7 @@ class M30SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE60"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 172
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -246,34 +210,23 @@ class M30SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VF30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VF30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M36S+ VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M36SPlusVG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.model = "M30S+ VG20"
|
||||
self.nominal_chips = 82
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -291,10 +244,7 @@ class M30SPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VG50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VG50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -312,10 +262,7 @@ class M30SPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VH10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 64
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -324,10 +271,7 @@ class M30SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VH20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -345,10 +289,7 @@ class M30SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VH40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 74
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -357,10 +298,7 @@ class M30SPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VH50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VH50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 64
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -24,10 +24,8 @@ class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 255
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,10 +34,8 @@ class M30SPlusPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 255
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -48,10 +44,7 @@ class M30SPlusPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VE30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 215
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -60,10 +53,7 @@ class M30SPlusPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VE40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VE40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 225
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -72,10 +62,7 @@ class M30SPlusPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VE50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 235
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -84,10 +71,7 @@ class M30SPlusPlusVF40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VF40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VF40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -126,10 +110,7 @@ class M30SPlusPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 82
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -138,10 +119,7 @@ class M30SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 86
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -150,10 +128,7 @@ class M30SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -162,10 +137,7 @@ class M30SPlusPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -201,10 +173,7 @@ class M30SPlusPlusVH80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 74
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -213,10 +182,7 @@ class M30SPlusPlusVH90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH90"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -24,10 +24,7 @@ class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31 V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,8 +33,5 @@ class M31V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31 V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 74
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -18,14 +18,19 @@ import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
class M31HV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31H V10"
|
||||
self.nominal_chips = 114
|
||||
self.fan_count = 0
|
||||
|
||||
class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31H V40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31HV40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 136
|
||||
self.fan_count = 0
|
||||
|
||||
27
pyasic/miners/types/whatsminer/M3X/M31L.py
Normal file
27
pyasic/miners/types/whatsminer/M3X/M31L.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
class M31LV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31L V10"
|
||||
self.nominal_chips = 114
|
||||
self.fan_count = 2
|
||||
@@ -42,10 +42,7 @@ class M31SV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -54,10 +51,7 @@ class M31SV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S V40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SV40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 123
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -66,10 +60,7 @@ class M31SV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S V50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SV50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -108,10 +99,7 @@ class M31SV90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S V90"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SV90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -129,10 +117,7 @@ class M31SVE20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S VE20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SVE20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 74
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -24,10 +24,7 @@ class M31SEV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31SE V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SEV10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 82
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,10 +33,7 @@ class M31SEV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31SE V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SEV20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -48,8 +42,5 @@ class M31SEV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31SE V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SEV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -24,10 +24,7 @@ class M31SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,10 +33,7 @@ class M31SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -66,10 +60,7 @@ class M31SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 148
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -114,10 +105,7 @@ class M31SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 82
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -135,10 +123,7 @@ class M31SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -147,10 +132,7 @@ class M31SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -159,10 +141,7 @@ class M31SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -195,10 +174,7 @@ class M31SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VF20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VF20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -219,10 +195,7 @@ class M31SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VG20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VG20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -231,10 +204,7 @@ class M31SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class M32V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M32 V10"
|
||||
self.nominal_chips = 74
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -24,10 +24,7 @@ class M33V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33 V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M33V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 33
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
@@ -36,10 +33,7 @@ class M33V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33 V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M33V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 62
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
@@ -48,8 +42,5 @@ class M33V30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33 V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M33V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -24,8 +24,6 @@ class M33SVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33S VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M33SVG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 116
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -18,16 +18,23 @@ import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
class M33SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33S+ VG20"
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 112
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
class M33SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33S+ VH20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 100
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
@@ -36,7 +43,8 @@ class M33SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M33S+ VH30"
|
||||
self.nominal_chips = 0
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 0 # slot1 116, slot2 106, slot3 116, slot4 106
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
|
||||
@@ -48,8 +48,5 @@ class M33SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
self.ip = ip
|
||||
self.model = "M33S++ VG40"
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VG40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 174
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -24,8 +24,6 @@ class M36SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M36S VE10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M36SVE10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 114
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -24,8 +24,6 @@ class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M36S+ VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M36S+ VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 108
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -24,8 +24,6 @@ class M36SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M36S++ VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M36S++ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 80
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -18,14 +18,28 @@ import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
class M39V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M39 V10"
|
||||
self.nominal_chips = 50
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
class M39V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M39 V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M39 V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 54
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
class M39V30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M39 V30"
|
||||
self.nominal_chips = 68
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .M30 import M30V10, M30V20
|
||||
from .M30K import M30KV10
|
||||
from .M30L import M30LV10
|
||||
from .M30S import (
|
||||
M30SV10,
|
||||
M30SV20,
|
||||
@@ -67,6 +69,7 @@ from .M30S_Plus import (
|
||||
M30SPlusVE100,
|
||||
M30SPlusVF20,
|
||||
M30SPlusVF30,
|
||||
M30SPlusVG20,
|
||||
M30SPlusVG30,
|
||||
M30SPlusVG40,
|
||||
M30SPlusVG50,
|
||||
@@ -77,7 +80,6 @@ from .M30S_Plus import (
|
||||
M30SPlusVH40,
|
||||
M30SPlusVH50,
|
||||
M30SPlusVH60,
|
||||
M36SPlusVG30,
|
||||
)
|
||||
from .M30S_Plus_Plus import (
|
||||
M30SPlusPlusV10,
|
||||
@@ -103,7 +105,8 @@ from .M30S_Plus_Plus import (
|
||||
M30SPlusPlusVJ30,
|
||||
)
|
||||
from .M31 import M31V10, M31V20
|
||||
from .M31H import M31HV40
|
||||
from .M31H import M31HV10, M31HV40
|
||||
from .M31L import M31LV10
|
||||
from .M31S import (
|
||||
M31SV10,
|
||||
M31SV20,
|
||||
@@ -145,10 +148,10 @@ from .M32 import M32V10, M32V20
|
||||
from .M32S import M32S
|
||||
from .M33 import M33V10, M33V20, M33V30
|
||||
from .M33S import M33SVG30
|
||||
from .M33S_Plus import M33SPlusVH20, M33SPlusVH30
|
||||
from .M33S_Plus import M33SPlusVG20, M33SPlusVH20, M33SPlusVH30
|
||||
from .M33S_Plus_Plus import M33SPlusPlusVG40, M33SPlusPlusVH20, M33SPlusPlusVH30
|
||||
from .M34S_Plus import M34SPlusVE10
|
||||
from .M36S import M36SVE10
|
||||
from .M36S_Plus import M36SPlusVG30
|
||||
from .M36S_Plus_Plus import M36SPlusPlusVH30
|
||||
from .M39 import M39V20
|
||||
from .M39 import M39V10, M39V20, M39V30
|
||||
|
||||
@@ -18,16 +18,21 @@ import warnings
|
||||
|
||||
from pyasic.miners.makes import WhatsMiner
|
||||
|
||||
class M50VE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M50 VE30"
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 255
|
||||
self.fan_count = 2
|
||||
|
||||
class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0"):
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M50 VG30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -36,10 +41,7 @@ class M50VH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M50 VH10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 86
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -57,10 +59,7 @@ class M50VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M50 VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -72,10 +72,7 @@ class M50SVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M50S VH20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 135
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -84,10 +81,7 @@ class M50SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M50S VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ class M53VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M53 VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M53 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 128
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -24,8 +24,6 @@ class M56VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
self.model = "M56 VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M56 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 108
|
||||
self.fan_count = 0
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .M50 import (
|
||||
M50VE30,
|
||||
M50VG30,
|
||||
M50VH10,
|
||||
M50VH20,
|
||||
|
||||
26
pyasic/miners/whatsminer/btminer/M2X/M20P.py
Normal file
26
pyasic/miners/whatsminer/btminer/M2X/M20P.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 M2X
|
||||
from pyasic.miners.types import M20PV10, M20PV30
|
||||
|
||||
|
||||
class BTMinerM20PV10(M2X, M20PV10):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM20PV30(M2X, M20PV30):
|
||||
pass
|
||||
@@ -15,6 +15,7 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .M20 import BTMinerM20V10
|
||||
from .M20P import BTMinerM20PV10, BTMinerM20PV30
|
||||
from .M20S import BTMinerM20SV10, BTMinerM20SV20, BTMinerM20SV30
|
||||
from .M20S_Plus import BTMinerM20SPlusV30
|
||||
from .M21 import BTMinerM21V10
|
||||
|
||||
22
pyasic/miners/whatsminer/btminer/M3X/M30K.py
Normal file
22
pyasic/miners/whatsminer/btminer/M3X/M30K.py
Normal 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 M3X
|
||||
from pyasic.miners.types import M30KV10
|
||||
|
||||
|
||||
class BTMinerM30KV10(M3X, M30KV10):
|
||||
pass
|
||||
22
pyasic/miners/whatsminer/btminer/M3X/M30L.py
Normal file
22
pyasic/miners/whatsminer/btminer/M3X/M30L.py
Normal 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 M3X
|
||||
from pyasic.miners.types import M30LV10
|
||||
|
||||
|
||||
class BTMinerM30LV10(M3X, M30LV10):
|
||||
pass
|
||||
@@ -36,6 +36,7 @@ from pyasic.miners.types import (
|
||||
M30SPlusVE100,
|
||||
M30SPlusVF20,
|
||||
M30SPlusVF30,
|
||||
M30SPlusVG20,
|
||||
M30SPlusVG30,
|
||||
M30SPlusVG40,
|
||||
M30SPlusVG50,
|
||||
@@ -46,7 +47,6 @@ from pyasic.miners.types import (
|
||||
M30SPlusVH40,
|
||||
M30SPlusVH50,
|
||||
M30SPlusVH60,
|
||||
M36SPlusVG30,
|
||||
)
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ class BTMinerM30SPlusVF30(M3X, M30SPlusVF30):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM36SPlusVG30(M3X, M36SPlusVG30):
|
||||
class BTMinerM30SPlusVG20(M3X, M30SPlusVG20):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from pyasic.miners.backends import M3X
|
||||
from pyasic.miners.types import M31HV40
|
||||
from pyasic.miners.types import M31HV10, M31HV40
|
||||
|
||||
|
||||
class BTMinerM31HV10(M3X, M31HV10):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM31HV40(M3X, M31HV40):
|
||||
|
||||
22
pyasic/miners/whatsminer/btminer/M3X/M31L.py
Normal file
22
pyasic/miners/whatsminer/btminer/M3X/M31L.py
Normal 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 M3X
|
||||
from pyasic.miners.types import M30LV10
|
||||
|
||||
|
||||
class BTMinerM31LV10(M3X, M30LV10):
|
||||
pass
|
||||
@@ -15,7 +15,11 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from pyasic.miners.backends import M3X
|
||||
from pyasic.miners.types import M33SPlusVH20, M33SPlusVH30
|
||||
from pyasic.miners.types import M33SPlusVG20, M33SPlusVH20, M33SPlusVH30
|
||||
|
||||
|
||||
class BTMinerM33SPlusVG20(M3X, M33SPlusVG20):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM33SPlusVH20(M3X, M33SPlusVH20):
|
||||
|
||||
@@ -15,8 +15,16 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from pyasic.miners.backends import M3X
|
||||
from pyasic.miners.types import M39V20
|
||||
from pyasic.miners.types import M39V10, M39V20, M39V30
|
||||
|
||||
|
||||
class BTMinerM39V10(M3X, M39V10):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM39V20(M3X, M39V20):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM39V30(M3X, M39V30):
|
||||
pass
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .M30 import BTMinerM30V10, BTMinerM30V20
|
||||
from .M30K import BTMinerM30KV10
|
||||
from .M30L import BTMinerM30LV10
|
||||
from .M30S import (
|
||||
BTMinerM30SV10,
|
||||
BTMinerM30SV20,
|
||||
@@ -67,6 +69,7 @@ from .M30S_Plus import (
|
||||
BTMinerM30SPlusVE100,
|
||||
BTMinerM30SPlusVF20,
|
||||
BTMinerM30SPlusVF30,
|
||||
BTMinerM30SPlusVG20,
|
||||
BTMinerM30SPlusVG30,
|
||||
BTMinerM30SPlusVG40,
|
||||
BTMinerM30SPlusVG50,
|
||||
@@ -77,7 +80,6 @@ from .M30S_Plus import (
|
||||
BTMinerM30SPlusVH40,
|
||||
BTMinerM30SPlusVH50,
|
||||
BTMinerM30SPlusVH60,
|
||||
BTMinerM36SPlusVG30,
|
||||
)
|
||||
from .M30S_Plus_Plus import (
|
||||
BTMinerM30SPlusPlusV10,
|
||||
@@ -103,7 +105,8 @@ from .M30S_Plus_Plus import (
|
||||
BTMinerM30SPlusPlusVJ30,
|
||||
)
|
||||
from .M31 import BTMinerM31V10, BTMinerM31V20
|
||||
from .M31H import BTMinerM31HV40
|
||||
from .M31H import BTMinerM31HV10, BTMinerM31HV40
|
||||
from .M31L import BTMinerM31LV10
|
||||
from .M31S import (
|
||||
BTMinerM31SV10,
|
||||
BTMinerM31SV20,
|
||||
@@ -145,7 +148,7 @@ from .M32 import BTMinerM32V10, BTMinerM32V20
|
||||
from .M32S import BTMinerM32S
|
||||
from .M33 import BTMinerM33V10, BTMinerM33V20, BTMinerM33V30
|
||||
from .M33S import BTMinerM33SVG30
|
||||
from .M33S_Plus import BTMinerM33SPlusVH20, BTMinerM33SPlusVH30
|
||||
from .M33S_Plus import BTMinerM33SPlusVG20, BTMinerM33SPlusVH20, BTMinerM33SPlusVH30
|
||||
from .M33S_Plus_Plus import (
|
||||
BTMinerM33SPlusPlusVG40,
|
||||
BTMinerM33SPlusPlusVH20,
|
||||
@@ -155,4 +158,4 @@ from .M34S_Plus import BTMinerM34SPlusVE10
|
||||
from .M36S import BTMinerM36SVE10
|
||||
from .M36S_Plus import BTMinerM36SPlusVG30
|
||||
from .M36S_Plus_Plus import BTMinerM36SPlusPlusVH30
|
||||
from .M39 import BTMinerM39V20
|
||||
from .M39 import BTMinerM39V10, BTMinerM39V20, BTMinerM39V30
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
from pyasic.miners.backends import M5X
|
||||
from pyasic.miners.types import (
|
||||
M50VE30,
|
||||
M50VG30,
|
||||
M50VH10,
|
||||
M50VH20,
|
||||
@@ -31,6 +32,10 @@ from pyasic.miners.types import (
|
||||
)
|
||||
|
||||
|
||||
class BTMinerM50VE30(M5X, M50VE30):
|
||||
pass
|
||||
|
||||
|
||||
class BTMinerM50VG30(M5X, M50VG30):
|
||||
pass
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .M50 import (
|
||||
BTMinerM50VE30,
|
||||
BTMinerM50VG30,
|
||||
BTMinerM50VH10,
|
||||
BTMinerM50VH20,
|
||||
|
||||
@@ -19,98 +19,113 @@ import ipaddress
|
||||
import logging
|
||||
from typing import AsyncIterator, List, Union
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.miners.miner_factory import AnyMiner, miner_factory
|
||||
from pyasic.network.net_range import MinerNetworkRange
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class MinerNetwork:
|
||||
"""A class to handle a network containing miners. Handles scanning and gets miners via [`MinerFactory`][pyasic.miners.miner_factory.MinerFactory].
|
||||
|
||||
Parameters:
|
||||
ip_addr: ### An IP address, range of IP addresses, or a list of IPs
|
||||
* Takes a single IP address as an `ipadddress.ipaddress()` or a string
|
||||
* Takes a string formatted as:
|
||||
```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}, ...]```
|
||||
mask: A subnet mask to use when constructing the network. Only used if `ip_addr` is a single IP.
|
||||
Defaults to /24 (255.255.255.0 or 0.0.0.255)
|
||||
hosts: A list of `ipaddress.IPv4Address` to be used when scanning.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ip_addr: Union[str, List[str], None] = None,
|
||||
mask: Union[str, int, None] = None,
|
||||
) -> None:
|
||||
self.network = None
|
||||
self.ip_addr = ip_addr
|
||||
self.connected_miners = {}
|
||||
if isinstance(mask, str):
|
||||
if mask.startswith("/"):
|
||||
mask = mask.replace("/", "")
|
||||
self.mask = mask
|
||||
self.network = self.get_network()
|
||||
def __init__(self, hosts: List[ipaddress.IPv4Address]):
|
||||
self.hosts = hosts
|
||||
|
||||
def __len__(self):
|
||||
return len([item for item in self.get_network().hosts()])
|
||||
return len(self.hosts)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.network)
|
||||
@classmethod
|
||||
def from_list(cls, addresses: list) -> "MinerNetwork":
|
||||
"""Parse a list of address constructors into a MinerNetwork.
|
||||
|
||||
def hosts(self):
|
||||
for x in self.network.hosts():
|
||||
yield x
|
||||
|
||||
def get_network(self) -> ipaddress.ip_network:
|
||||
"""Get the network using the information passed to the MinerNetwork or from cache.
|
||||
|
||||
Returns:
|
||||
The proper network to be able to scan.
|
||||
Parameters:
|
||||
addresses: A list of address constructors, such as `["10.1-2.1.1-50", "10.4.1-2.1-50"]`.
|
||||
"""
|
||||
# if we have a network cached already, use that
|
||||
if self.network:
|
||||
return self.network
|
||||
hosts = []
|
||||
for address in addresses:
|
||||
hosts = [*hosts, *cls.from_address(address).hosts]
|
||||
return cls(sorted(list(set(hosts))))
|
||||
|
||||
# if there is no IP address passed, default to 192.168.1.0
|
||||
if not self.ip_addr:
|
||||
self.ip_addr = "192.168.1.0"
|
||||
if "-" in self.ip_addr:
|
||||
self.network = MinerNetworkRange(self.ip_addr)
|
||||
elif isinstance(self.ip_addr, list):
|
||||
self.network = MinerNetworkRange(self.ip_addr)
|
||||
else:
|
||||
# if there is no subnet mask passed, default to /24
|
||||
if not self.mask:
|
||||
subnet_mask = "24"
|
||||
# if we do have a mask passed, use that
|
||||
else:
|
||||
subnet_mask = str(self.mask)
|
||||
@classmethod
|
||||
def from_address(cls, address: str) -> "MinerNetwork":
|
||||
"""Parse an address constructor into a MinerNetwork.
|
||||
|
||||
# save the network and return it
|
||||
self.network = ipaddress.ip_network(
|
||||
f"{self.ip_addr}/{subnet_mask}", strict=False
|
||||
)
|
||||
Parameters:
|
||||
address: An address constructor, such as `"10.1-2.1.1-50"`.
|
||||
"""
|
||||
octets = address.split(".")
|
||||
if len(octets) > 4:
|
||||
raise ValueError("Too many octets in IP constructor.")
|
||||
if len(octets) < 4:
|
||||
raise ValueError("Too few octets in IP constructor.")
|
||||
return cls.from_octets(*octets)
|
||||
|
||||
logging.debug(f"{self} - (Get Network) - Found network")
|
||||
return self.network
|
||||
@classmethod
|
||||
def from_octets(
|
||||
cls, oct_1: str, oct_2: str, oct_3: str, oct_4: str
|
||||
) -> "MinerNetwork":
|
||||
"""Parse 4 octet constructors into a MinerNetwork.
|
||||
|
||||
async def scan_network_for_miners(self) -> List[AnyMiner]:
|
||||
"""Scan the network for miners, and return found miners as a list.
|
||||
Parameters:
|
||||
oct_1: An octet constructor, such as `"10"`.
|
||||
oct_2: An octet constructor, such as `"1-2"`.
|
||||
oct_3: An octet constructor, such as `"1"`.
|
||||
oct_4: An octet constructor, such as `"1-50"`.
|
||||
"""
|
||||
|
||||
hosts = []
|
||||
|
||||
oct_1_val_start, oct_1_start, oct_1_end = compute_oct_range(oct_1)
|
||||
for oct_1_idx in range((abs(oct_1_end - oct_1_start)) + 1):
|
||||
oct_1_val = str(oct_1_idx + oct_1_start)
|
||||
|
||||
oct_2_val_start, oct_2_start, oct_2_end = compute_oct_range(oct_2)
|
||||
for oct_2_idx in range((abs(oct_2_end - oct_2_start)) + 1):
|
||||
oct_2_val = str(oct_2_idx + oct_2_start)
|
||||
|
||||
oct_3_val_start, oct_3_start, oct_3_end = compute_oct_range(oct_3)
|
||||
for oct_3_idx in range((abs(oct_3_end - oct_3_start)) + 1):
|
||||
oct_3_val = str(oct_3_idx + oct_3_start)
|
||||
|
||||
oct_4_val_start, oct_4_start, oct_4_end = compute_oct_range(oct_4)
|
||||
for oct_4_idx in range((abs(oct_4_end - oct_4_start)) + 1):
|
||||
oct_4_val = str(oct_4_idx + oct_4_start)
|
||||
|
||||
hosts.append(
|
||||
ipaddress.ip_address(
|
||||
".".join([oct_1_val, oct_2_val, oct_3_val, oct_4_val])
|
||||
)
|
||||
)
|
||||
return cls(sorted(hosts))
|
||||
|
||||
@classmethod
|
||||
def from_subnet(cls, subnet: str) -> "MinerNetwork":
|
||||
"""Parse a subnet into a MinerNetwork.
|
||||
|
||||
Parameters:
|
||||
subnet: A subnet string, such as `"10.0.0.1/24"`.
|
||||
"""
|
||||
return cls(list(ipaddress.ip_network(subnet, strict=False).hosts()))
|
||||
|
||||
async def scan(self) -> List[AnyMiner]:
|
||||
"""Scan the network for miners.
|
||||
|
||||
Returns:
|
||||
A list of found miners.
|
||||
"""
|
||||
# get the network
|
||||
local_network = self.get_network()
|
||||
return await self.scan_network_for_miners()
|
||||
|
||||
async def scan_network_for_miners(self) -> List[AnyMiner]:
|
||||
logging.debug(f"{self} - (Scan Network For Miners) - Scanning")
|
||||
|
||||
# clear cached miners
|
||||
miner_factory.clear_cached_miners()
|
||||
|
||||
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
|
||||
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
|
||||
miners = await asyncio.gather(
|
||||
*[self.ping_and_get_miner(host, limit) for host in local_network.hosts()]
|
||||
*[self.ping_and_get_miner(host, limit) for host in self.hosts]
|
||||
)
|
||||
|
||||
# remove all None from the miner list
|
||||
@@ -132,15 +147,12 @@ class MinerNetwork:
|
||||
# get the current event loop
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# get the network
|
||||
local_network = self.get_network()
|
||||
|
||||
# create a list of scan tasks
|
||||
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
|
||||
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
|
||||
miners = asyncio.as_completed(
|
||||
[
|
||||
loop.create_task(self.ping_and_get_miner(host, limit))
|
||||
for host in local_network.hosts()
|
||||
for host in self.hosts
|
||||
]
|
||||
)
|
||||
for miner in miners:
|
||||
@@ -149,83 +161,33 @@ class MinerNetwork:
|
||||
except TimeoutError:
|
||||
yield None
|
||||
|
||||
@staticmethod
|
||||
async def ping_miner(
|
||||
ip: ipaddress.ip_address, semaphore: asyncio.Semaphore
|
||||
) -> Union[None, ipaddress.ip_address]:
|
||||
async with semaphore:
|
||||
try:
|
||||
miner = await ping_miner(ip)
|
||||
if miner:
|
||||
return miner
|
||||
except ConnectionRefusedError:
|
||||
tasks = [ping_miner(ip, port=port) for port in [4029, 8889]]
|
||||
for miner in asyncio.as_completed(tasks):
|
||||
try:
|
||||
miner = await miner
|
||||
if miner:
|
||||
return miner
|
||||
except ConnectionRefusedError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
async def ping_and_get_miner(
|
||||
ip: ipaddress.ip_address, semaphore: asyncio.Semaphore
|
||||
) -> Union[None, AnyMiner]:
|
||||
async with semaphore:
|
||||
try:
|
||||
miner = await ping_and_get_miner(ip)
|
||||
if miner:
|
||||
return miner
|
||||
return await ping_and_get_miner(ip)
|
||||
except ConnectionRefusedError:
|
||||
tasks = [ping_and_get_miner(ip, port=port) for port in [4029, 8889]]
|
||||
tasks = [
|
||||
ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]
|
||||
]
|
||||
for miner in asyncio.as_completed(tasks):
|
||||
try:
|
||||
miner = await miner
|
||||
if miner:
|
||||
return miner
|
||||
return await miner
|
||||
except ConnectionRefusedError:
|
||||
pass
|
||||
|
||||
|
||||
async def ping_miner(
|
||||
ip: ipaddress.ip_address, port=4028
|
||||
) -> Union[None, ipaddress.ip_address]:
|
||||
for i in range(PyasicSettings().network_ping_retries):
|
||||
try:
|
||||
connection_fut = asyncio.open_connection(str(ip), port)
|
||||
# get the read and write streams from the connection
|
||||
reader, writer = await asyncio.wait_for(
|
||||
connection_fut, timeout=PyasicSettings().network_ping_timeout
|
||||
)
|
||||
# immediately close connection, we know connection happened
|
||||
writer.close()
|
||||
# make sure the writer is closed
|
||||
await writer.wait_closed()
|
||||
# ping was successful
|
||||
return ip
|
||||
except asyncio.exceptions.TimeoutError:
|
||||
# ping failed if we time out
|
||||
continue
|
||||
except (ConnectionRefusedError, OSError):
|
||||
# handle for other connection errors
|
||||
logging.debug(f"{str(ip)}: Connection Refused.")
|
||||
raise ConnectionRefusedError
|
||||
except Exception as e:
|
||||
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
|
||||
raise ConnectionRefusedError
|
||||
return
|
||||
|
||||
|
||||
async def ping_and_get_miner(
|
||||
ip: ipaddress.ip_address, port=4028
|
||||
ip: ipaddress.ip_address, port=80
|
||||
) -> Union[None, AnyMiner]:
|
||||
for i in range(PyasicSettings().network_ping_retries):
|
||||
for i in range(settings.get("network_ping_retries", 1)):
|
||||
try:
|
||||
connection_fut = asyncio.open_connection(str(ip), port)
|
||||
# get the read and write streams from the connection
|
||||
reader, writer = await asyncio.wait_for(
|
||||
connection_fut, timeout=PyasicSettings().network_ping_timeout
|
||||
connection_fut, timeout=settings.get("network_ping_timeout", 3)
|
||||
)
|
||||
# immediately close connection, we know connection happened
|
||||
writer.close()
|
||||
@@ -236,11 +198,25 @@ async def ping_and_get_miner(
|
||||
except asyncio.exceptions.TimeoutError:
|
||||
# ping failed if we time out
|
||||
continue
|
||||
except (ConnectionRefusedError, OSError):
|
||||
# handle for other connection errors
|
||||
logging.debug(f"{str(ip)}: Connection Refused.")
|
||||
raise ConnectionRefusedError
|
||||
except OSError as e:
|
||||
raise ConnectionRefusedError from e
|
||||
except Exception as e:
|
||||
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
|
||||
raise ConnectionRefusedError
|
||||
logging.warning(f"{str(ip)}: Unhandled ping exception: {e}")
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
def compute_oct_range(octet: str) -> tuple:
|
||||
octet_split = octet.split("-")
|
||||
octet_start = int(octet_split[0])
|
||||
octet_end = None
|
||||
try:
|
||||
octet_end = int(octet_split[1])
|
||||
except IndexError:
|
||||
pass
|
||||
if octet_end is None:
|
||||
octet_end = int(octet_start)
|
||||
|
||||
octet_val_start = min([octet_start, octet_end])
|
||||
|
||||
return octet_val_start, octet_start, octet_end
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import ipaddress
|
||||
from typing import Union
|
||||
|
||||
|
||||
class MinerNetworkRange:
|
||||
"""A MinerNetwork that takes a range of IP addresses.
|
||||
|
||||
Parameters:
|
||||
ip_range: ## A range of IP addresses to put in the network, or a list of IPs
|
||||
* Takes a string formatted as:
|
||||
```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]):
|
||||
self.host_ips = []
|
||||
if isinstance(ip_range, str):
|
||||
ip_ranges = ip_range.replace(" ", "").split(",")
|
||||
for item in ip_ranges:
|
||||
if "-" in item:
|
||||
start, end = item.split("-")
|
||||
start_ip = ipaddress.ip_address(start)
|
||||
end_ip = ipaddress.ip_address(end)
|
||||
networks = ipaddress.summarize_address_range(start_ip, end_ip)
|
||||
for network in networks:
|
||||
self.host_ips.append(network.network_address)
|
||||
for host in network.hosts():
|
||||
if host not in self.host_ips:
|
||||
self.host_ips.append(host)
|
||||
if network.broadcast_address not in self.host_ips:
|
||||
self.host_ips.append(network.broadcast_address)
|
||||
else:
|
||||
self.host_ips.append(ipaddress.ip_address(item))
|
||||
elif isinstance(ip_range, list):
|
||||
self.host_ips = [ipaddress.ip_address(ip_str) for ip_str in ip_range]
|
||||
|
||||
def hosts(self):
|
||||
for x in self.host_ips:
|
||||
yield x
|
||||
@@ -13,28 +13,47 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import socket
|
||||
import struct
|
||||
from ssl import SSLContext
|
||||
from typing import Any, Union
|
||||
|
||||
from dataclasses import dataclass
|
||||
import httpx
|
||||
from httpx import AsyncHTTPTransport
|
||||
|
||||
from pyasic.misc import Singleton
|
||||
_settings = { # defaults
|
||||
"network_ping_retries": 1,
|
||||
"network_ping_timeout": 3,
|
||||
"network_scan_threads": 300,
|
||||
"factory_get_retries": 1,
|
||||
"factory_get_timeout": 3,
|
||||
"get_data_retries": 1,
|
||||
"api_function_timeout": 5,
|
||||
"default_whatsminer_password": "admin",
|
||||
"default_innosilicon_password": "admin",
|
||||
"default_antminer_password": "root",
|
||||
"default_bosminer_password": "root",
|
||||
"default_vnish_password": "admin",
|
||||
"default_goldshell_password": "123456789",
|
||||
"socket_linger_time": 1000,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class PyasicSettings(metaclass=Singleton):
|
||||
network_ping_retries: int = 1
|
||||
network_ping_timeout: int = 3
|
||||
network_scan_threads: int = 300
|
||||
ssl_cxt = httpx.create_ssl_context()
|
||||
|
||||
miner_factory_get_version_retries: int = 1
|
||||
|
||||
miner_get_data_retries: int = 1
|
||||
def transport(verify: Union[str, bool, SSLContext] = ssl_cxt):
|
||||
l_onoff = 1
|
||||
l_linger = get("so_linger_time", 1000)
|
||||
|
||||
global_whatsminer_password = "admin"
|
||||
global_innosilicon_password = "admin"
|
||||
global_antminer_password = "root"
|
||||
global_bosminer_password = "root"
|
||||
global_vnish_password = "admin"
|
||||
global_goldshell_password = "123456789"
|
||||
opts = [(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", l_onoff, l_linger))]
|
||||
|
||||
debug: bool = False
|
||||
logfile: bool = False
|
||||
return AsyncHTTPTransport(socket_options=opts, verify=verify)
|
||||
|
||||
|
||||
def get(key: str, other: Any = None) -> Any:
|
||||
return _settings.get(key, other)
|
||||
|
||||
|
||||
def update(key: str, val: Any) -> Any:
|
||||
_settings[key] = val
|
||||
|
||||
@@ -24,7 +24,7 @@ from pyasic.errors import APIWarning
|
||||
class BaseWebAPI(ABC):
|
||||
def __init__(self, ip: str) -> None:
|
||||
# ip address of the miner
|
||||
self.ip = ip # ipaddress.ip_address(ip)
|
||||
self.ip = ip # ipaddress.ip_address(ip)
|
||||
self.username = "root"
|
||||
self.pwd = "root"
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@ from typing import Union
|
||||
|
||||
import httpx
|
||||
|
||||
from pyasic.settings import PyasicSettings
|
||||
from pyasic import settings
|
||||
from pyasic.web import BaseWebAPI
|
||||
|
||||
|
||||
class AntminerModernWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.pwd = PyasicSettings().global_antminer_password
|
||||
self.pwd = settings.get("default_antminer_password", "root")
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
@@ -38,10 +38,13 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
if parameters:
|
||||
data = await client.post(
|
||||
url, data=json.dumps(parameters), auth=auth, timeout=15 # noqa
|
||||
url,
|
||||
data=json.dumps(parameters),
|
||||
auth=auth,
|
||||
timeout=settings.get("api_function_timeout", 3), # noqa
|
||||
)
|
||||
else:
|
||||
data = await client.get(url, auth=auth)
|
||||
@@ -57,7 +60,7 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
async def multicommand(
|
||||
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||
) -> dict:
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
tasks = [
|
||||
asyncio.create_task(self._handle_multicommand(client, command))
|
||||
for command in commands
|
||||
@@ -137,7 +140,7 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
class AntminerOldWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.pwd = PyasicSettings().global_antminer_password
|
||||
self.pwd = settings.get("default_antminer_password", "root")
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
@@ -149,10 +152,13 @@ class AntminerOldWebAPI(BaseWebAPI):
|
||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
if parameters:
|
||||
data = await client.post(
|
||||
url, data=parameters, auth=auth, timeout=15
|
||||
url,
|
||||
data=parameters,
|
||||
auth=auth,
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
)
|
||||
else:
|
||||
data = await client.get(url, auth=auth)
|
||||
@@ -170,7 +176,7 @@ class AntminerOldWebAPI(BaseWebAPI):
|
||||
) -> dict:
|
||||
data = {k: None for k in commands}
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
for command in commands:
|
||||
try:
|
||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import json
|
||||
from typing import Union
|
||||
|
||||
import httpx
|
||||
|
||||
from pyasic import APIError
|
||||
from pyasic.settings import PyasicSettings
|
||||
from pyasic.web import BaseWebAPI
|
||||
|
||||
|
||||
class BOSMinerWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.pwd = PyasicSettings().global_bosminer_password
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, dict],
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
if isinstance(command, str):
|
||||
return await self.send_luci_command(command)
|
||||
else:
|
||||
return await self.send_gql_command(command)
|
||||
|
||||
def parse_command(self, graphql_command: Union[dict, set]) -> str:
|
||||
if isinstance(graphql_command, dict):
|
||||
data = []
|
||||
for key in graphql_command:
|
||||
if graphql_command[key] is not None:
|
||||
parsed = self.parse_command(graphql_command[key])
|
||||
data.append(key + parsed)
|
||||
else:
|
||||
data.append(key)
|
||||
else:
|
||||
data = graphql_command
|
||||
return "{" + ",".join(data) + "}"
|
||||
|
||||
async def send_gql_command(
|
||||
self,
|
||||
command: dict,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}/graphql"
|
||||
query = command
|
||||
if command.get("query") is None:
|
||||
query = {"query": self.parse_command(command)}
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await self.auth(client)
|
||||
data = await client.post(url, json=query)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if data.status_code == 200:
|
||||
try:
|
||||
return data.json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: Union[dict, str], allow_warning: bool = True
|
||||
) -> dict:
|
||||
luci_commands = []
|
||||
gql_commands = []
|
||||
for cmd in commands:
|
||||
if isinstance(cmd, dict):
|
||||
gql_commands.append(cmd)
|
||||
if isinstance(cmd, str):
|
||||
luci_commands.append(cmd)
|
||||
|
||||
luci_data = await self.luci_multicommand(*luci_commands)
|
||||
gql_data = await self.gql_multicommand(*gql_commands)
|
||||
|
||||
if gql_data is None:
|
||||
gql_data = {}
|
||||
if luci_data is None:
|
||||
luci_data = {}
|
||||
|
||||
data = dict(**luci_data, **gql_data)
|
||||
return data
|
||||
|
||||
async def luci_multicommand(self, *commands: str) -> dict:
|
||||
data = {}
|
||||
for command in commands:
|
||||
data[command] = await self.send_luci_command(command, ignore_errors=True)
|
||||
return data
|
||||
|
||||
async def gql_multicommand(self, *commands: dict) -> dict:
|
||||
def merge(*d: dict):
|
||||
ret = {}
|
||||
for i in d:
|
||||
if i:
|
||||
for k in i:
|
||||
if not k in ret:
|
||||
ret[k] = i[k]
|
||||
else:
|
||||
ret[k] = merge(ret[k], i[k])
|
||||
return None if ret == {} else ret
|
||||
|
||||
command = merge(*commands)
|
||||
data = await self.send_command(command)
|
||||
if data is not None:
|
||||
if data.get("data") is None:
|
||||
try:
|
||||
commands = list(commands)
|
||||
# noinspection PyTypeChecker
|
||||
commands.remove({"bos": {"faultLight": None}})
|
||||
command = merge(*commands)
|
||||
data = await self.send_gql_command(command)
|
||||
except (LookupError, ValueError):
|
||||
pass
|
||||
if not data:
|
||||
data = {}
|
||||
data["multicommand"] = False
|
||||
return data
|
||||
|
||||
async def auth(self, client: httpx.AsyncClient) -> None:
|
||||
url = f"http://{self.ip}/graphql"
|
||||
await client.post(
|
||||
url,
|
||||
json={
|
||||
"query": 'mutation{auth{login(username:"'
|
||||
+ "root"
|
||||
+ '", password:"'
|
||||
+ self.pwd
|
||||
+ '"){__typename}}}'
|
||||
},
|
||||
)
|
||||
|
||||
async def send_luci_command(self, path: str, ignore_errors: bool = False) -> dict:
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await self.luci_auth(client)
|
||||
data = await client.get(
|
||||
f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"}
|
||||
)
|
||||
if data.status_code == 200:
|
||||
return data.json()
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(
|
||||
f"Web command failed: path={path}, code={data.status_code}"
|
||||
)
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(f"Web command failed: path={path}")
|
||||
|
||||
async def luci_auth(self, session: httpx.AsyncClient):
|
||||
login = {"luci_username": self.username, "luci_password": self.pwd}
|
||||
url = f"http://{self.ip}/cgi-bin/luci"
|
||||
headers = {
|
||||
"User-Agent": "BTC Tools v0.1", # only seems to respond if this user-agent is set
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
}
|
||||
await session.post(url, headers=headers, data=login)
|
||||
|
||||
async def get_net_conf(self):
|
||||
return await self.send_luci_command(
|
||||
"/cgi-bin/luci/admin/network/iface_status/lan"
|
||||
)
|
||||
|
||||
async def get_cfg_metadata(self):
|
||||
return await self.send_luci_command("/cgi-bin/luci/admin/miner/cfg_metadata")
|
||||
|
||||
async def get_cfg_data(self):
|
||||
return await self.send_luci_command("/cgi-bin/luci/admin/miner/cfg_data")
|
||||
|
||||
async def get_bos_info(self):
|
||||
return await self.send_luci_command("/cgi-bin/luci/bos/info")
|
||||
|
||||
async def get_overview(self):
|
||||
return await self.send_luci_command(
|
||||
"/cgi-bin/luci/admin/status/overview?status=1"
|
||||
) # needs status=1 or it fails
|
||||
|
||||
async def get_api_status(self):
|
||||
return await self.send_luci_command("/cgi-bin/luci/admin/miner/api_status")
|
||||
626
pyasic/web/bosminer/__init__.py
Normal file
626
pyasic/web/bosminer/__init__.py
Normal file
@@ -0,0 +1,626 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Union
|
||||
|
||||
import grpc_requests
|
||||
import httpx
|
||||
from google.protobuf.message import Message
|
||||
from grpc import RpcError
|
||||
|
||||
from pyasic import APIError, settings
|
||||
from pyasic.web import BaseWebAPI
|
||||
from pyasic.web.bosminer.proto import (
|
||||
get_auth_service_descriptors,
|
||||
get_service_descriptors,
|
||||
)
|
||||
from pyasic.web.bosminer.proto.bos.v1.actions_pb2 import ( # noqa: this will be defined
|
||||
SetLocateDeviceStatusRequest,
|
||||
)
|
||||
from pyasic.web.bosminer.proto.bos.v1.authentication_pb2 import ( # noqa: this will be defined
|
||||
SetPasswordRequest,
|
||||
)
|
||||
from pyasic.web.bosminer.proto.bos.v1.common_pb2 import ( # noqa: this will be defined
|
||||
SaveAction,
|
||||
)
|
||||
from pyasic.web.bosminer.proto.bos.v1.cooling_pb2 import ( # noqa: this will be defined
|
||||
SetImmersionModeRequest,
|
||||
)
|
||||
from pyasic.web.bosminer.proto.bos.v1.miner_pb2 import ( # noqa: this will be defined
|
||||
DisableHashboardsRequest,
|
||||
EnableHashboardsRequest,
|
||||
)
|
||||
from pyasic.web.bosminer.proto.bos.v1.performance_pb2 import ( # noqa: this will be defined
|
||||
DecrementHashrateTargetRequest,
|
||||
DecrementPowerTargetRequest,
|
||||
IncrementHashrateTargetRequest,
|
||||
IncrementPowerTargetRequest,
|
||||
SetDefaultHashrateTargetRequest,
|
||||
SetDefaultPowerTargetRequest,
|
||||
SetHashrateTargetRequest,
|
||||
SetPowerTargetRequest,
|
||||
)
|
||||
|
||||
|
||||
class BOSMinerWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str, boser: bool = None) -> None:
|
||||
if boser is None:
|
||||
boser = True
|
||||
|
||||
if boser:
|
||||
self.gql = BOSMinerGQLAPI(
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
self.grpc = BOSMinerGRPCAPI(
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
else:
|
||||
self.gql = None
|
||||
self.grpc = None
|
||||
self.luci = BOSMinerLuCIAPI(
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
|
||||
self._pwd = settings.get("default_bosminer_password", "root")
|
||||
super().__init__(ip)
|
||||
|
||||
@property
|
||||
def pwd(self):
|
||||
return self._pwd
|
||||
|
||||
@pwd.setter
|
||||
def pwd(self, other: str):
|
||||
self._pwd = other
|
||||
self.luci.pwd = other
|
||||
if self.gql is not None:
|
||||
self.gql.pwd = other
|
||||
if self.grpc is not None:
|
||||
self.grpc.pwd = other
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, dict],
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
if isinstance(command, dict):
|
||||
if self.gql is not None:
|
||||
return await self.gql.send_command(command)
|
||||
elif command.startswith("/cgi-bin/luci"):
|
||||
return await self.gql.send_command(command)
|
||||
else:
|
||||
if self.grpc is not None:
|
||||
return await self.grpc.send_command(command)
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: Union[dict, str], allow_warning: bool = True
|
||||
) -> dict:
|
||||
luci_commands = []
|
||||
gql_commands = []
|
||||
grpc_commands = []
|
||||
for cmd in commands:
|
||||
if isinstance(cmd, dict):
|
||||
gql_commands.append(cmd)
|
||||
elif cmd.startswith("/cgi-bin/luci"):
|
||||
luci_commands.append(cmd)
|
||||
else:
|
||||
grpc_commands.append(cmd)
|
||||
|
||||
luci_data = await self.luci.multicommand(*luci_commands)
|
||||
if self.gql is not None:
|
||||
gql_data = await self.gql.multicommand(*gql_commands)
|
||||
else:
|
||||
gql_data = None
|
||||
if self.grpc is not None:
|
||||
grpc_data = await self.grpc.multicommand(*grpc_commands)
|
||||
else:
|
||||
grpc_data = None
|
||||
|
||||
if gql_data is None:
|
||||
gql_data = {}
|
||||
if luci_data is None:
|
||||
luci_data = {}
|
||||
if grpc_data is None:
|
||||
grpc_data = {}
|
||||
|
||||
data = dict(**luci_data, **gql_data, **grpc_data)
|
||||
return data
|
||||
|
||||
|
||||
class BOSMinerGQLAPI:
|
||||
def __init__(self, ip: str, pwd: str):
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
|
||||
async def multicommand(self, *commands: dict) -> dict:
|
||||
def merge(*d: dict):
|
||||
ret = {}
|
||||
for i in d:
|
||||
if i:
|
||||
for k in i:
|
||||
if not k in ret:
|
||||
ret[k] = i[k]
|
||||
else:
|
||||
ret[k] = merge(ret[k], i[k])
|
||||
return None if ret == {} else ret
|
||||
|
||||
command = merge(*commands)
|
||||
data = await self.send_command(command)
|
||||
if data is not None:
|
||||
if data.get("data") is None:
|
||||
try:
|
||||
commands = list(commands)
|
||||
# noinspection PyTypeChecker
|
||||
commands.remove({"bos": {"faultLight": None}})
|
||||
command = merge(*commands)
|
||||
data = await self.send_command(command)
|
||||
except (LookupError, ValueError):
|
||||
pass
|
||||
if not data:
|
||||
data = {}
|
||||
data["multicommand"] = False
|
||||
return data
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: dict,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}/graphql"
|
||||
query = command
|
||||
if command.get("query") is None:
|
||||
query = {"query": self.parse_command(command)}
|
||||
try:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
await self.auth(client)
|
||||
data = await client.post(url, json=query)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if data.status_code == 200:
|
||||
try:
|
||||
return data.json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
|
||||
def parse_command(self, graphql_command: Union[dict, set]) -> str:
|
||||
if isinstance(graphql_command, dict):
|
||||
data = []
|
||||
for key in graphql_command:
|
||||
if graphql_command[key] is not None:
|
||||
parsed = self.parse_command(graphql_command[key])
|
||||
data.append(key + parsed)
|
||||
else:
|
||||
data.append(key)
|
||||
else:
|
||||
data = graphql_command
|
||||
return "{" + ",".join(data) + "}"
|
||||
|
||||
async def auth(self, client: httpx.AsyncClient) -> None:
|
||||
url = f"http://{self.ip}/graphql"
|
||||
await client.post(
|
||||
url,
|
||||
json={
|
||||
"query": 'mutation{auth{login(username:"'
|
||||
+ "root"
|
||||
+ '", password:"'
|
||||
+ self.pwd
|
||||
+ '"){__typename}}}'
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class BOSMinerLuCIAPI:
|
||||
def __init__(self, ip: str, pwd: str):
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
|
||||
async def multicommand(self, *commands: str) -> dict:
|
||||
data = {}
|
||||
for command in commands:
|
||||
data[command] = await self.send_command(command, ignore_errors=True)
|
||||
return data
|
||||
|
||||
async def send_command(self, path: str, ignore_errors: bool = False) -> dict:
|
||||
try:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
await self.auth(client)
|
||||
data = await client.get(
|
||||
f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"}
|
||||
)
|
||||
if data.status_code == 200:
|
||||
return data.json()
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(
|
||||
f"Web command failed: path={path}, code={data.status_code}"
|
||||
)
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(f"Web command failed: path={path}")
|
||||
|
||||
async def auth(self, session: httpx.AsyncClient):
|
||||
login = {"luci_username": self.username, "luci_password": self.pwd}
|
||||
url = f"http://{self.ip}/cgi-bin/luci"
|
||||
headers = {
|
||||
"User-Agent": "BTC Tools v0.1", # only seems to respond if this user-agent is set
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
}
|
||||
await session.post(url, headers=headers, data=login)
|
||||
|
||||
async def get_net_conf(self):
|
||||
return await self.send_command("/cgi-bin/luci/admin/network/iface_status/lan")
|
||||
|
||||
async def get_cfg_metadata(self):
|
||||
return await self.send_command("/cgi-bin/luci/admin/miner/cfg_metadata")
|
||||
|
||||
async def get_cfg_data(self):
|
||||
return await self.send_command("/cgi-bin/luci/admin/miner/cfg_data")
|
||||
|
||||
async def get_bos_info(self):
|
||||
return await self.send_command("/cgi-bin/luci/bos/info")
|
||||
|
||||
async def get_overview(self):
|
||||
return await self.send_command(
|
||||
"/cgi-bin/luci/admin/status/overview?status=1"
|
||||
) # needs status=1 or it fails
|
||||
|
||||
async def get_api_status(self):
|
||||
return await self.send_command("/cgi-bin/luci/admin/miner/api_status")
|
||||
|
||||
|
||||
class BOSMinerGRPCAPI:
|
||||
def __init__(self, ip: str, pwd: str):
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self._auth = None
|
||||
self._auth_time = datetime.now()
|
||||
|
||||
@property
|
||||
def commands(self) -> list:
|
||||
return self.get_commands()
|
||||
|
||||
def get_commands(self) -> list:
|
||||
return [
|
||||
func
|
||||
for func in
|
||||
# each function in self
|
||||
dir(self)
|
||||
if func
|
||||
not in ["send_command", "multicommand", "auth", "commands", "get_commands"]
|
||||
if callable(getattr(self, func)) and
|
||||
# no __ or _ methods
|
||||
not func.startswith("__") and not func.startswith("_")
|
||||
]
|
||||
|
||||
async def multicommand(self, *commands: str) -> dict:
|
||||
pass
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: str,
|
||||
message: Message = None,
|
||||
ignore_errors: bool = False,
|
||||
auth: bool = True,
|
||||
) -> dict:
|
||||
service, method = command.split("/")
|
||||
metadata = []
|
||||
if auth:
|
||||
metadata.append(("authorization", await self.auth()))
|
||||
async with grpc_requests.StubAsyncClient(
|
||||
f"{self.ip}:50051", service_descriptors=get_service_descriptors()
|
||||
) as client:
|
||||
await client.register_all_service()
|
||||
try:
|
||||
return await client.request(
|
||||
service,
|
||||
method,
|
||||
request=message,
|
||||
metadata=metadata,
|
||||
)
|
||||
except RpcError as e:
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError(e._details)
|
||||
|
||||
async def auth(self):
|
||||
if self._auth is not None and self._auth_time - datetime.now() < timedelta(
|
||||
seconds=3540
|
||||
):
|
||||
return self._auth
|
||||
await self._get_auth()
|
||||
return self._auth
|
||||
|
||||
async def _get_auth(self):
|
||||
async with grpc_requests.StubAsyncClient(
|
||||
f"{self.ip}:50051", service_descriptors=get_auth_service_descriptors()
|
||||
) as client:
|
||||
await client.register_all_service()
|
||||
method_meta = client.get_method_meta(
|
||||
"braiins.bos.v1.AuthenticationService", "Login"
|
||||
)
|
||||
_request = method_meta.method_type.request_parser(
|
||||
{"username": self.username, "password": self.pwd},
|
||||
method_meta.input_type,
|
||||
)
|
||||
metadata = await method_meta.handler(_request).initial_metadata()
|
||||
|
||||
for key, value in metadata:
|
||||
if key == "authorization":
|
||||
self._auth = value
|
||||
self._auth_time = datetime.now()
|
||||
return self._auth
|
||||
|
||||
async def get_api_version(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.ApiVersionService/GetApiVersion", auth=False
|
||||
)
|
||||
|
||||
async def start(self):
|
||||
return await self.send_command("braiins.bos.v1.ActionsService/Start")
|
||||
|
||||
async def stop(self):
|
||||
return await self.send_command("braiins.bos.v1.ActionsService/Stop")
|
||||
|
||||
async def pause_mining(self):
|
||||
return await self.send_command("braiins.bos.v1.ActionsService/PauseMining")
|
||||
|
||||
async def resume_mining(self):
|
||||
return await self.send_command("braiins.bos.v1.ActionsService/ResumeMining")
|
||||
|
||||
async def restart(self):
|
||||
return await self.send_command("braiins.bos.v1.ActionsService/Restart")
|
||||
|
||||
async def reboot(self):
|
||||
return await self.send_command("braiins.bos.v1.ActionsService/Reboot")
|
||||
|
||||
async def set_locate_device_status(self, enable: bool):
|
||||
message = SetLocateDeviceStatusRequest()
|
||||
message.enable = enable
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.ActionsService/SetLocateDeviceStatus", message=message
|
||||
)
|
||||
|
||||
async def get_locate_device_status(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.ActionsService/GetLocateDeviceStatus"
|
||||
)
|
||||
|
||||
async def set_password(self, password: str = None):
|
||||
message = SetPasswordRequest()
|
||||
if password:
|
||||
message.password = password
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.AuthenticationService/SetPassword", message=message
|
||||
)
|
||||
|
||||
async def get_cooling_state(self):
|
||||
return await self.send_command("braiins.bos.v1.CoolingService/GetCoolingState")
|
||||
|
||||
async def set_immersion_mode(
|
||||
self,
|
||||
enable: bool,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = SetImmersionModeRequest()
|
||||
message.enable = enable
|
||||
message.save_action = save_action
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.CoolingService/SetImmersionMode", message=message
|
||||
)
|
||||
|
||||
async def get_tuner_state(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/GetTunerState"
|
||||
)
|
||||
|
||||
async def list_target_profiles(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/ListTargetProfiles"
|
||||
)
|
||||
|
||||
async def set_default_power_target(
|
||||
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
|
||||
):
|
||||
message = SetDefaultPowerTargetRequest()
|
||||
message.save_action = save_action
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/SetDefaultPowerTarget", message=message
|
||||
)
|
||||
|
||||
async def set_power_target(
|
||||
self,
|
||||
power_target: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = SetPowerTargetRequest()
|
||||
message.power_target.watt = power_target
|
||||
message.save_action = save_action
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/SetPowerTarget", message=message
|
||||
)
|
||||
|
||||
async def increment_power_target(
|
||||
self,
|
||||
power_target_increment: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = IncrementPowerTargetRequest()
|
||||
message.power_target_increment.watt = power_target_increment
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/IncrementPowerTarget", message=message
|
||||
)
|
||||
|
||||
async def decrement_power_target(
|
||||
self,
|
||||
power_target_decrement: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = DecrementPowerTargetRequest()
|
||||
message.power_target_decrement.watt = power_target_decrement
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/DecrementPowerTarget",
|
||||
message=message,
|
||||
)
|
||||
|
||||
async def set_default_hashrate_target(
|
||||
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
|
||||
):
|
||||
message = SetDefaultHashrateTargetRequest()
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget",
|
||||
message=message,
|
||||
)
|
||||
|
||||
async def set_hashrate_target(
|
||||
self,
|
||||
hashrate_target: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = SetHashrateTargetRequest()
|
||||
message.hashrate_target.terahash_per_second = hashrate_target
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/SetHashrateTarget", message=message
|
||||
)
|
||||
|
||||
async def increment_hashrate_target(
|
||||
self,
|
||||
hashrate_target_increment: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = IncrementHashrateTargetRequest()
|
||||
message.hashrate_target_increment.terahash_per_second = (
|
||||
hashrate_target_increment
|
||||
)
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/IncrementHashrateTarget",
|
||||
message=message,
|
||||
)
|
||||
|
||||
async def decrement_hashrate_target(
|
||||
self,
|
||||
hashrate_target_decrement: int,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = DecrementHashrateTargetRequest()
|
||||
message.hashrate_target_decrement.terahash_per_second = (
|
||||
hashrate_target_decrement
|
||||
)
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/DecrementHashrateTarget",
|
||||
message=message,
|
||||
)
|
||||
|
||||
async def set_dps(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PerformanceService/SetDPS")
|
||||
|
||||
async def set_performance_mode(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/SetPerformanceMode"
|
||||
)
|
||||
|
||||
async def get_active_performance_mode(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/GetActivePerformanceMode"
|
||||
)
|
||||
|
||||
async def get_pool_groups(self):
|
||||
return await self.send_command("braiins.bos.v1.PoolService/GetPoolGroups")
|
||||
|
||||
async def create_pool_group(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PoolService/CreatePoolGroup")
|
||||
|
||||
async def update_pool_group(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PoolService/UpdatePoolGroup")
|
||||
|
||||
async def remove_pool_group(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PoolService/RemovePoolGroup")
|
||||
|
||||
async def get_miner_configuration(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.ConfigurationService/GetMinerConfiguration"
|
||||
)
|
||||
|
||||
async def get_constraints(self):
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.ConfigurationService/GetConstraints"
|
||||
)
|
||||
|
||||
async def get_license_state(self):
|
||||
return await self.send_command("braiins.bos.v1.LicenseService/GetLicenseState")
|
||||
|
||||
async def get_miner_status(self):
|
||||
return await self.send_command("braiins.bos.v1.MinerService/GetMinerStatus")
|
||||
|
||||
async def get_miner_details(self):
|
||||
return await self.send_command("braiins.bos.v1.MinerService/GetMinerDetails")
|
||||
|
||||
async def get_miner_stats(self):
|
||||
return await self.send_command("braiins.bos.v1.MinerService/GetMinerStats")
|
||||
|
||||
async def get_hashboards(self):
|
||||
return await self.send_command("braiins.bos.v1.MinerService/GetHashboards")
|
||||
|
||||
async def get_support_archive(self):
|
||||
return await self.send_command("braiins.bos.v1.MinerService/GetSupportArchive")
|
||||
|
||||
async def enable_hashboards(
|
||||
self,
|
||||
hashboard_ids: List[str],
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = EnableHashboardsRequest()
|
||||
message.hashboard_ids[:] = hashboard_ids
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.MinerService/EnableHashboards", message=message
|
||||
)
|
||||
|
||||
async def disable_hashboards(
|
||||
self,
|
||||
hashboard_ids: List[str],
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
message = DisableHashboardsRequest()
|
||||
message.hashboard_ids[:] = hashboard_ids
|
||||
message.save_action = save_action
|
||||
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.MinerService/DisableHashboards", message=message
|
||||
)
|
||||
54
pyasic/web/bosminer/proto/__init__.py
Normal file
54
pyasic/web/bosminer/proto/__init__.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 __future__ import annotations
|
||||
|
||||
from .bos import version_pb2
|
||||
from .bos.v1 import (
|
||||
actions_pb2,
|
||||
authentication_pb2,
|
||||
common_pb2,
|
||||
configuration_pb2,
|
||||
constraints_pb2,
|
||||
cooling_pb2,
|
||||
license_pb2,
|
||||
miner_pb2,
|
||||
performance_pb2,
|
||||
pool_pb2,
|
||||
units_pb2,
|
||||
work_pb2,
|
||||
)
|
||||
|
||||
|
||||
def get_service_descriptors():
|
||||
return [
|
||||
*version_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*authentication_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*actions_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*common_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*configuration_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*constraints_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*cooling_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*license_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*miner_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*performance_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*pool_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*units_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
*work_pb2.DESCRIPTOR.services_by_name.values(),
|
||||
]
|
||||
|
||||
|
||||
def get_auth_service_descriptors():
|
||||
return authentication_pb2.DESCRIPTOR.services_by_name.values()
|
||||
15
pyasic/web/bosminer/proto/bos/__init__.py
Normal file
15
pyasic/web/bosminer/proto/bos/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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. -
|
||||
# ------------------------------------------------------------------------------
|
||||
15
pyasic/web/bosminer/proto/bos/v1/__init__.py
Normal file
15
pyasic/web/bosminer/proto/bos/v1/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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. -
|
||||
# ------------------------------------------------------------------------------
|
||||
23
pyasic/web/bosminer/proto/bos/v1/actions.py
Normal file
23
pyasic/web/bosminer/proto/bos/v1/actions.py
Normal 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 enum import Enum
|
||||
|
||||
|
||||
class SaveAction(Enum):
|
||||
UNSPECIFIED = "SaveAction.SAVE_ACTION_UNSPECIFIED"
|
||||
SAVE = "SaveAction.SAVE_ACTION_SAVE"
|
||||
SAVE_AND_APPLY = "SaveAction.SAVE_ACTION_SAVE_AND_APPLY"
|
||||
SAVE_AND_FORCE_APPLY = "SaveAction.SAVE_ACTION_SAVE_AND_FORCE_APPLY"
|
||||
56
pyasic/web/bosminer/proto/bos/v1/actions_pb2.py
Normal file
56
pyasic/web/bosminer/proto/bos/v1/actions_pb2.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/actions.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\x14\x62os/v1/actions.proto\x12\x0e\x62raiins.bos.v1"\x0e\n\x0cStartRequest"(\n\rStartResponse\x12\x17\n\x0f\x61lready_running\x18\x01 \x01(\x08"\x10\n\x0eRestartRequest"*\n\x0fRestartResponse\x12\x17\n\x0f\x61lready_running\x18\x01 \x01(\x08"\x0f\n\rRebootRequest"\x10\n\x0eRebootResponse"\r\n\x0bStopRequest"\'\n\x0cStopResponse\x12\x17\n\x0f\x61lready_stopped\x18\x01 \x01(\x08"\x14\n\x12PauseMiningRequest"-\n\x13PauseMiningResponse\x12\x16\n\x0e\x61lready_paused\x18\x01 \x01(\x08"\x15\n\x13ResumeMiningRequest".\n\x14ResumeMiningResponse\x12\x16\n\x0e\x61lready_mining\x18\x01 \x01(\x08".\n\x1cSetLocateDeviceStatusRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08"-\n\x1aLocateDeviceStatusResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08"\x1e\n\x1cGetLocateDeviceStatusRequest2\xc7\x05\n\x0e\x41\x63tionsService\x12\x44\n\x05Start\x12\x1c.braiins.bos.v1.StartRequest\x1a\x1d.braiins.bos.v1.StartResponse\x12\x41\n\x04Stop\x12\x1b.braiins.bos.v1.StopRequest\x1a\x1c.braiins.bos.v1.StopResponse\x12V\n\x0bPauseMining\x12".braiins.bos.v1.PauseMiningRequest\x1a#.braiins.bos.v1.PauseMiningResponse\x12Y\n\x0cResumeMining\x12#.braiins.bos.v1.ResumeMiningRequest\x1a$.braiins.bos.v1.ResumeMiningResponse\x12J\n\x07Restart\x12\x1e.braiins.bos.v1.RestartRequest\x1a\x1f.braiins.bos.v1.RestartResponse\x12G\n\x06Reboot\x12\x1d.braiins.bos.v1.RebootRequest\x1a\x1e.braiins.bos.v1.RebootResponse\x12q\n\x15SetLocateDeviceStatus\x12,.braiins.bos.v1.SetLocateDeviceStatusRequest\x1a*.braiins.bos.v1.LocateDeviceStatusResponse\x12q\n\x15GetLocateDeviceStatus\x12,.braiins.bos.v1.GetLocateDeviceStatusRequest\x1a*.braiins.bos.v1.LocateDeviceStatusResponseb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.actions_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_STARTREQUEST"]._serialized_start = 40
|
||||
_globals["_STARTREQUEST"]._serialized_end = 54
|
||||
_globals["_STARTRESPONSE"]._serialized_start = 56
|
||||
_globals["_STARTRESPONSE"]._serialized_end = 96
|
||||
_globals["_RESTARTREQUEST"]._serialized_start = 98
|
||||
_globals["_RESTARTREQUEST"]._serialized_end = 114
|
||||
_globals["_RESTARTRESPONSE"]._serialized_start = 116
|
||||
_globals["_RESTARTRESPONSE"]._serialized_end = 158
|
||||
_globals["_REBOOTREQUEST"]._serialized_start = 160
|
||||
_globals["_REBOOTREQUEST"]._serialized_end = 175
|
||||
_globals["_REBOOTRESPONSE"]._serialized_start = 177
|
||||
_globals["_REBOOTRESPONSE"]._serialized_end = 193
|
||||
_globals["_STOPREQUEST"]._serialized_start = 195
|
||||
_globals["_STOPREQUEST"]._serialized_end = 208
|
||||
_globals["_STOPRESPONSE"]._serialized_start = 210
|
||||
_globals["_STOPRESPONSE"]._serialized_end = 249
|
||||
_globals["_PAUSEMININGREQUEST"]._serialized_start = 251
|
||||
_globals["_PAUSEMININGREQUEST"]._serialized_end = 271
|
||||
_globals["_PAUSEMININGRESPONSE"]._serialized_start = 273
|
||||
_globals["_PAUSEMININGRESPONSE"]._serialized_end = 318
|
||||
_globals["_RESUMEMININGREQUEST"]._serialized_start = 320
|
||||
_globals["_RESUMEMININGREQUEST"]._serialized_end = 341
|
||||
_globals["_RESUMEMININGRESPONSE"]._serialized_start = 343
|
||||
_globals["_RESUMEMININGRESPONSE"]._serialized_end = 389
|
||||
_globals["_SETLOCATEDEVICESTATUSREQUEST"]._serialized_start = 391
|
||||
_globals["_SETLOCATEDEVICESTATUSREQUEST"]._serialized_end = 437
|
||||
_globals["_LOCATEDEVICESTATUSRESPONSE"]._serialized_start = 439
|
||||
_globals["_LOCATEDEVICESTATUSRESPONSE"]._serialized_end = 484
|
||||
_globals["_GETLOCATEDEVICESTATUSREQUEST"]._serialized_start = 486
|
||||
_globals["_GETLOCATEDEVICESTATUSREQUEST"]._serialized_end = 516
|
||||
_globals["_ACTIONSSERVICE"]._serialized_start = 519
|
||||
_globals["_ACTIONSSERVICE"]._serialized_end = 1230
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
36
pyasic/web/bosminer/proto/bos/v1/authentication_pb2.py
Normal file
36
pyasic/web/bosminer/proto/bos/v1/authentication_pb2.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/authentication.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\x1b\x62os/v1/authentication.proto\x12\x0e\x62raiins.bos.v1"2\n\x0cLoginRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t"\x0f\n\rLoginResponse"8\n\x12SetPasswordRequest\x12\x15\n\x08password\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0b\n\t_password"\x15\n\x13SetPasswordResponse2\xb5\x01\n\x15\x41uthenticationService\x12\x44\n\x05Login\x12\x1c.braiins.bos.v1.LoginRequest\x1a\x1d.braiins.bos.v1.LoginResponse\x12V\n\x0bSetPassword\x12".braiins.bos.v1.SetPasswordRequest\x1a#.braiins.bos.v1.SetPasswordResponseb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(
|
||||
DESCRIPTOR, "bos.v1.authentication_pb2", _globals
|
||||
)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_LOGINREQUEST"]._serialized_start = 47
|
||||
_globals["_LOGINREQUEST"]._serialized_end = 97
|
||||
_globals["_LOGINRESPONSE"]._serialized_start = 99
|
||||
_globals["_LOGINRESPONSE"]._serialized_end = 114
|
||||
_globals["_SETPASSWORDREQUEST"]._serialized_start = 116
|
||||
_globals["_SETPASSWORDREQUEST"]._serialized_end = 172
|
||||
_globals["_SETPASSWORDRESPONSE"]._serialized_start = 174
|
||||
_globals["_SETPASSWORDRESPONSE"]._serialized_end = 195
|
||||
_globals["_AUTHENTICATIONSERVICE"]._serialized_start = 198
|
||||
_globals["_AUTHENTICATIONSERVICE"]._serialized_end = 379
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
26
pyasic/web/bosminer/proto/bos/v1/common_pb2.py
Normal file
26
pyasic/web/bosminer/proto/bos/v1/common_pb2.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/common.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b"\n\x13\x62os/v1/common.proto\x12\x0e\x62raiins.bos.v1*\x85\x01\n\nSaveAction\x12\x1b\n\x17SAVE_ACTION_UNSPECIFIED\x10\x00\x12\x14\n\x10SAVE_ACTION_SAVE\x10\x01\x12\x1e\n\x1aSAVE_ACTION_SAVE_AND_APPLY\x10\x02\x12$\n SAVE_ACTION_SAVE_AND_FORCE_APPLY\x10\x03\x62\x06proto3"
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.common_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_SAVEACTION"]._serialized_start = 40
|
||||
_globals["_SAVEACTION"]._serialized_end = 173
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
40
pyasic/web/bosminer/proto/bos/v1/configuration_pb2.py
Normal file
40
pyasic/web/bosminer/proto/bos/v1/configuration_pb2.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/configuration.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from ...bos.v1 import cooling_pb2 as bos_dot_v1_dot_cooling__pb2
|
||||
from ...bos.v1 import performance_pb2 as bos_dot_v1_dot_performance__pb2
|
||||
from ...bos.v1 import pool_pb2 as bos_dot_v1_dot_pool__pb2
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\x1a\x62os/v1/configuration.proto\x12\x0e\x62raiins.bos.v1\x1a\x14\x62os/v1/cooling.proto\x1a\x18\x62os/v1/performance.proto\x1a\x11\x62os/v1/pool.proto"\x1e\n\x1cGetMinerConfigurationRequest"\xc6\x02\n\x1dGetMinerConfigurationResponse\x12;\n\x0bpool_groups\x18\x01 \x03(\x0b\x32&.braiins.bos.v1.PoolGroupConfiguration\x12\x39\n\x0btemperature\x18\x02 \x01(\x0b\x32$.braiins.bos.v1.CoolingConfiguration\x12\x31\n\x05tuner\x18\x03 \x01(\x0b\x32".braiins.bos.v1.TunerConfiguration\x12-\n\x03\x64ps\x18\x04 \x01(\x0b\x32 .braiins.bos.v1.DPSConfiguration\x12K\n\x10hashboard_config\x18\x05 \x01(\x0b\x32\x31.braiins.bos.v1.HashboardPerformanceConfiguration"\x17\n\x15GetConstraintsRequest"\x95\x02\n\x16GetConstraintsResponse\x12;\n\x11tuner_constraints\x18\x01 \x01(\x0b\x32 .braiins.bos.v1.TunerConstraints\x12?\n\x13\x63ooling_constraints\x18\x02 \x01(\x0b\x32".braiins.bos.v1.CoolingConstraints\x12\x37\n\x0f\x64ps_constraints\x18\x03 \x01(\x0b\x32\x1e.braiins.bos.v1.DPSConstraints\x12\x44\n\x16hashboards_constraints\x18\x04 \x01(\x0b\x32$.braiins.bos.v1.HashboardConstraints2\xed\x01\n\x14\x43onfigurationService\x12t\n\x15GetMinerConfiguration\x12,.braiins.bos.v1.GetMinerConfigurationRequest\x1a-.braiins.bos.v1.GetMinerConfigurationResponse\x12_\n\x0eGetConstraints\x12%.braiins.bos.v1.GetConstraintsRequest\x1a&.braiins.bos.v1.GetConstraintsResponseb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(
|
||||
DESCRIPTOR, "bos.v1.configuration_pb2", _globals
|
||||
)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_GETMINERCONFIGURATIONREQUEST"]._serialized_start = 113
|
||||
_globals["_GETMINERCONFIGURATIONREQUEST"]._serialized_end = 143
|
||||
_globals["_GETMINERCONFIGURATIONRESPONSE"]._serialized_start = 146
|
||||
_globals["_GETMINERCONFIGURATIONRESPONSE"]._serialized_end = 472
|
||||
_globals["_GETCONSTRAINTSREQUEST"]._serialized_start = 474
|
||||
_globals["_GETCONSTRAINTSREQUEST"]._serialized_end = 497
|
||||
_globals["_GETCONSTRAINTSRESPONSE"]._serialized_start = 500
|
||||
_globals["_GETCONSTRAINTSRESPONSE"]._serialized_end = 777
|
||||
_globals["_CONFIGURATIONSERVICE"]._serialized_start = 780
|
||||
_globals["_CONFIGURATIONSERVICE"]._serialized_end = 1017
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
44
pyasic/web/bosminer/proto/bos/v1/constraints_pb2.py
Normal file
44
pyasic/web/bosminer/proto/bos/v1/constraints_pb2.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/constraints.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\x18\x62os/v1/constraints.proto\x12\x0e\x62raiins.bos.v1\x1a\x12\x62os/v1/units.proto">\n\x11UInt32Constraints\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\r\x12\x0b\n\x03min\x18\x02 \x01(\r\x12\x0b\n\x03max\x18\x03 \x01(\r">\n\x11\x44oubleConstraints\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x01\x12\x0b\n\x03min\x18\x02 \x01(\x01\x12\x0b\n\x03max\x18\x03 \x01(\x01"\x82\x01\n\x10PowerConstraints\x12&\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12"\n\x03min\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12"\n\x03max\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x9a\x01\n\x13HashrateConstraints\x12-\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12)\n\x03min\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12)\n\x03max\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x9a\x01\n\x16TemperatureConstraints\x12,\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12(\n\x03min\x18\x02 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12(\n\x03max\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature"$\n\x11\x42ooleanConstraint\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x08"\x85\x01\n\x13\x44urationConstraints\x12&\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Hours\x12"\n\x03min\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Hours\x12"\n\x03max\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.Hours"\x92\x01\n\x14\x46requencyConstraints\x12*\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12&\n\x03min\x18\x02 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12&\n\x03max\x18\x03 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency"\x8a\x01\n\x12VoltageConstraints\x12(\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12$\n\x03min\x18\x02 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12$\n\x03max\x18\x03 \x01(\x0b\x32\x17.braiins.bos.v1.Voltageb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.constraints_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_UINT32CONSTRAINTS"]._serialized_start = 64
|
||||
_globals["_UINT32CONSTRAINTS"]._serialized_end = 126
|
||||
_globals["_DOUBLECONSTRAINTS"]._serialized_start = 128
|
||||
_globals["_DOUBLECONSTRAINTS"]._serialized_end = 190
|
||||
_globals["_POWERCONSTRAINTS"]._serialized_start = 193
|
||||
_globals["_POWERCONSTRAINTS"]._serialized_end = 323
|
||||
_globals["_HASHRATECONSTRAINTS"]._serialized_start = 326
|
||||
_globals["_HASHRATECONSTRAINTS"]._serialized_end = 480
|
||||
_globals["_TEMPERATURECONSTRAINTS"]._serialized_start = 483
|
||||
_globals["_TEMPERATURECONSTRAINTS"]._serialized_end = 637
|
||||
_globals["_BOOLEANCONSTRAINT"]._serialized_start = 639
|
||||
_globals["_BOOLEANCONSTRAINT"]._serialized_end = 675
|
||||
_globals["_DURATIONCONSTRAINTS"]._serialized_start = 678
|
||||
_globals["_DURATIONCONSTRAINTS"]._serialized_end = 811
|
||||
_globals["_FREQUENCYCONSTRAINTS"]._serialized_start = 814
|
||||
_globals["_FREQUENCYCONSTRAINTS"]._serialized_end = 960
|
||||
_globals["_VOLTAGECONSTRAINTS"]._serialized_start = 963
|
||||
_globals["_VOLTAGECONSTRAINTS"]._serialized_end = 1101
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
56
pyasic/web/bosminer/proto/bos/v1/cooling_pb2.py
Normal file
56
pyasic/web/bosminer/proto/bos/v1/cooling_pb2.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/cooling.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from ...bos.v1 import common_pb2 as bos_dot_v1_dot_common__pb2
|
||||
from ...bos.v1 import constraints_pb2 as bos_dot_v1_dot_constraints__pb2
|
||||
from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\x14\x62os/v1/cooling.proto\x12\x0e\x62raiins.bos.v1\x1a\x13\x62os/v1/common.proto\x1a\x18\x62os/v1/constraints.proto\x1a\x12\x62os/v1/units.proto"\xbc\x01\n\x0f\x43oolingAutoMode\x12\x37\n\x12target_temperature\x18\x01 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12\x34\n\x0fhot_temperature\x18\x02 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12:\n\x15\x64\x61ngerous_temperature\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature"\xb7\x01\n\x11\x43oolingManualMode\x12\x1c\n\x0f\x66\x61n_speed_ratio\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x34\n\x0fhot_temperature\x18\x02 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12:\n\x15\x64\x61ngerous_temperature\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.TemperatureB\x12\n\x10_fan_speed_ratio"G\n\x13\x43oolingDisabledMode\x12\x1c\n\x0f\x66\x61n_speed_ratio\x18\x01 \x01(\x01H\x00\x88\x01\x01\x42\x12\n\x10_fan_speed_ratio"\xfb\x01\n\x14\x43oolingConfiguration\x12"\n\x15minimum_required_fans\x18\x01 \x01(\rH\x01\x88\x01\x01\x12/\n\x04\x61uto\x18\x02 \x01(\x0b\x32\x1f.braiins.bos.v1.CoolingAutoModeH\x00\x12\x33\n\x06manual\x18\x03 \x01(\x0b\x32!.braiins.bos.v1.CoolingManualModeH\x00\x12\x37\n\x08\x64isabled\x18\x04 \x01(\x0b\x32#.braiins.bos.v1.CoolingDisabledModeH\x00\x42\x06\n\x04modeB\x18\n\x16_minimum_required_fans"\x99\x03\n\x12\x43oolingConstraints\x12\x39\n\x14\x64\x65\x66\x61ult_cooling_mode\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.CoolingMode\x12\x42\n\x12target_temperature\x18\x02 \x01(\x0b\x32&.braiins.bos.v1.TemperatureConstraints\x12?\n\x0fhot_temperature\x18\x03 \x01(\x0b\x32&.braiins.bos.v1.TemperatureConstraints\x12\x45\n\x15\x64\x61ngerous_temperature\x18\x04 \x01(\x0b\x32&.braiins.bos.v1.TemperatureConstraints\x12:\n\x0f\x66\x61n_speed_ratio\x18\x05 \x01(\x0b\x32!.braiins.bos.v1.DoubleConstraints\x12@\n\x15minimum_required_fans\x18\x06 \x01(\x0b\x32!.braiins.bos.v1.UInt32Constraints"s\n\x08\x46\x61nState\x12\x15\n\x08position\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x0b\n\x03rpm\x18\x02 \x01(\r\x12\x1f\n\x12target_speed_ratio\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x0b\n\t_positionB\x15\n\x13_target_speed_ratio"\x8f\x01\n\x11TemperatureSensor\x12\x0f\n\x02id\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x30\n\x08location\x18\x02 \x01(\x0e\x32\x1e.braiins.bos.v1.SensorLocation\x12\x30\n\x0btemperature\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.TemperatureB\x05\n\x03_id"\x18\n\x16GetCoolingStateRequest"\x81\x01\n\x17GetCoolingStateResponse\x12&\n\x04\x66\x61ns\x18\x01 \x03(\x0b\x32\x18.braiins.bos.v1.FanState\x12>\n\x13highest_temperature\x18\x02 \x01(\x0b\x32!.braiins.bos.v1.TemperatureSensor"i\n\x17SetImmersionModeRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x1d\n\x15\x65nable_immersion_mode\x18\x02 \x01(\x08"2\n\x18SetImmersionModeResponse\x12\x16\n\x0eimmersion_mode\x18\x01 \x01(\x08*v\n\x0b\x43oolingMode\x12\x1c\n\x18\x43OOLING_MODE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x43OOLING_MODE_AUTO\x10\x01\x12\x17\n\x13\x43OOLING_MODE_MANUAL\x10\x02\x12\x19\n\x15\x43OOLING_MODE_DISABLED\x10\x03*d\n\x0eSensorLocation\x12\x1f\n\x1bSENSOR_LOCATION_UNSPECIFIED\x10\x00\x12\x18\n\x14SENSOR_LOCATION_CHIP\x10\x01\x12\x17\n\x13SENSOR_LOCATION_PCB\x10\x02\x32\xdb\x01\n\x0e\x43oolingService\x12\x62\n\x0fGetCoolingState\x12&.braiins.bos.v1.GetCoolingStateRequest\x1a\'.braiins.bos.v1.GetCoolingStateResponse\x12\x65\n\x10SetImmersionMode\x12\'.braiins.bos.v1.SetImmersionModeRequest\x1a(.braiins.bos.v1.SetImmersionModeResponseb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.cooling_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_COOLINGMODE"]._serialized_start = 1803
|
||||
_globals["_COOLINGMODE"]._serialized_end = 1921
|
||||
_globals["_SENSORLOCATION"]._serialized_start = 1923
|
||||
_globals["_SENSORLOCATION"]._serialized_end = 2023
|
||||
_globals["_COOLINGAUTOMODE"]._serialized_start = 108
|
||||
_globals["_COOLINGAUTOMODE"]._serialized_end = 296
|
||||
_globals["_COOLINGMANUALMODE"]._serialized_start = 299
|
||||
_globals["_COOLINGMANUALMODE"]._serialized_end = 482
|
||||
_globals["_COOLINGDISABLEDMODE"]._serialized_start = 484
|
||||
_globals["_COOLINGDISABLEDMODE"]._serialized_end = 555
|
||||
_globals["_COOLINGCONFIGURATION"]._serialized_start = 558
|
||||
_globals["_COOLINGCONFIGURATION"]._serialized_end = 809
|
||||
_globals["_COOLINGCONSTRAINTS"]._serialized_start = 812
|
||||
_globals["_COOLINGCONSTRAINTS"]._serialized_end = 1221
|
||||
_globals["_FANSTATE"]._serialized_start = 1223
|
||||
_globals["_FANSTATE"]._serialized_end = 1338
|
||||
_globals["_TEMPERATURESENSOR"]._serialized_start = 1341
|
||||
_globals["_TEMPERATURESENSOR"]._serialized_end = 1484
|
||||
_globals["_GETCOOLINGSTATEREQUEST"]._serialized_start = 1486
|
||||
_globals["_GETCOOLINGSTATEREQUEST"]._serialized_end = 1510
|
||||
_globals["_GETCOOLINGSTATERESPONSE"]._serialized_start = 1513
|
||||
_globals["_GETCOOLINGSTATERESPONSE"]._serialized_end = 1642
|
||||
_globals["_SETIMMERSIONMODEREQUEST"]._serialized_start = 1644
|
||||
_globals["_SETIMMERSIONMODEREQUEST"]._serialized_end = 1749
|
||||
_globals["_SETIMMERSIONMODERESPONSE"]._serialized_start = 1751
|
||||
_globals["_SETIMMERSIONMODERESPONSE"]._serialized_end = 1801
|
||||
_globals["_COOLINGSERVICE"]._serialized_start = 2026
|
||||
_globals["_COOLINGSERVICE"]._serialized_end = 2245
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
42
pyasic/web/bosminer/proto/bos/v1/license_pb2.py
Normal file
42
pyasic/web/bosminer/proto/bos/v1/license_pb2.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: bos/v1/license.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\x14\x62os/v1/license.proto\x12\x0e\x62raiins.bos.v1\x1a\x12\x62os/v1/units.proto")\n\x0bNoneLicense\x12\x1a\n\x12time_to_restricted\x18\x01 \x01(\r"\x10\n\x0eLimitedLicense"\x9a\x01\n\x0cValidLicense\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.LicenseType\x12\x15\n\rcontract_name\x18\x02 \x01(\t\x12\x1a\n\x12time_to_restricted\x18\x03 \x01(\r\x12,\n\x07\x64\x65v_fee\x18\x04 \x01(\x0b\x32\x1b.braiins.bos.v1.BasesPoints"\x80\x01\n\x0e\x45xpiredLicense\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.LicenseType\x12\x15\n\rcontract_name\x18\x02 \x01(\t\x12,\n\x07\x64\x65v_fee\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.BasesPoints"\x18\n\x16GetLicenseStateRequest"\xe4\x01\n\x17GetLicenseStateResponse\x12+\n\x04none\x18\x01 \x01(\x0b\x32\x1b.braiins.bos.v1.NoneLicenseH\x00\x12\x31\n\x07limited\x18\x02 \x01(\x0b\x32\x1e.braiins.bos.v1.LimitedLicenseH\x00\x12-\n\x05valid\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.ValidLicenseH\x00\x12\x31\n\x07\x65xpired\x18\x04 \x01(\x0b\x32\x1e.braiins.bos.v1.ExpiredLicenseH\x00\x42\x07\n\x05state*_\n\x0bLicenseType\x12\x1c\n\x18LICENSE_TYPE_UNSPECIFIED\x10\x00\x12\x19\n\x15LICENSE_TYPE_STANDARD\x10\x01\x12\x17\n\x13LICENSE_TYPE_CUSTOM\x10\x02\x32t\n\x0eLicenseService\x12\x62\n\x0fGetLicenseState\x12&.braiins.bos.v1.GetLicenseStateRequest\x1a\'.braiins.bos.v1.GetLicenseStateResponseb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.license_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_LICENSETYPE"]._serialized_start = 666
|
||||
_globals["_LICENSETYPE"]._serialized_end = 761
|
||||
_globals["_NONELICENSE"]._serialized_start = 60
|
||||
_globals["_NONELICENSE"]._serialized_end = 101
|
||||
_globals["_LIMITEDLICENSE"]._serialized_start = 103
|
||||
_globals["_LIMITEDLICENSE"]._serialized_end = 119
|
||||
_globals["_VALIDLICENSE"]._serialized_start = 122
|
||||
_globals["_VALIDLICENSE"]._serialized_end = 276
|
||||
_globals["_EXPIREDLICENSE"]._serialized_start = 279
|
||||
_globals["_EXPIREDLICENSE"]._serialized_end = 407
|
||||
_globals["_GETLICENSESTATEREQUEST"]._serialized_start = 409
|
||||
_globals["_GETLICENSESTATEREQUEST"]._serialized_end = 433
|
||||
_globals["_GETLICENSESTATERESPONSE"]._serialized_start = 436
|
||||
_globals["_GETLICENSESTATERESPONSE"]._serialized_end = 664
|
||||
_globals["_LICENSESERVICE"]._serialized_start = 763
|
||||
_globals["_LICENSESERVICE"]._serialized_end = 879
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
84
pyasic/web/bosminer/proto/bos/v1/miner_pb2.py
Normal file
84
pyasic/web/bosminer/proto/bos/v1/miner_pb2.py
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user